+++ /dev/null
-Subproject commit 1edf9f656850c0c64dae260960fabd8249ea9c60
--- /dev/null
+.deps/*
+.libs/*
+*.lo
+*.o
+.dirstamp
+Makefile-bsdiff.am.inc
+AUTHORS
+NEWS
+README
+ChangeLog
+COPYING
--- /dev/null
+ Copyright 2003-2005 Colin Percival
+ Copyright 2012 Matthew Endsley
+ All rights reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted providing that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+# Copyright (C) 2015 Giuseppe Scrivano <gscrivan@redhat.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted providing that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+EXTRA_DIST += $(libbsdiff_srcpath)/bsdiff.h $(libbsdiff_srcpath)/bspatch.h $(libbsdiff_srcpath)/LICENSE $(libbsdiff_srcpath)/README.md
+
+libbsdiff_la_SOURCES = \
+ $(libbsdiff_srcpath)/bsdiff.c \
+ $(libbsdiff_srcpath)/bspatch.c \
+ $(NULL)
+
+libbsdiff_la_CFLAGS = $(AM_CFLAGS)
--- /dev/null
+bin_PROGRAMS = bsdiff bspatch
+
+bsdiff_SOURCES = bsdiff.c
+
+bspatch_SOURCES = bspatch.c
+
+bsdiff_CFLAGS = -DBSDIFF_EXECUTABLE
+bspatch_CFLAGS = -DBSPATCH_EXECUTABLE
+
+EXTRA_DIST = bsdiff.h bspatch.h
+
--- /dev/null
+bsdiff/bspatch
+==============
+bsdiff and bspatch are libraries for building and applying patches to binary
+files.
+
+The original algorithm and implementation was developed by Colin Percival. The
+algorithm is detailed in his (unpublished) paper, [Naïve Differences of Executable Code](http://www.daemonology.net/papers/bsdiff.pdf). For more information, visit his
+website at <http://www.daemonology.net/bsdiff/>.
+
+I maintain this project seperately from Colin's work, with the goal of making
+the core functionality easily embedable in existing projects.
+
+Contact
+-------
+[@MatthewEndsley](https://twitter.com/#!/MatthewEndsley)
+<https://github.com/mendsley/bsdiff>
+
+License
+-------
+Copyright 2003-2005 Colin Percival
+Copyright 2012 Matthew Endsley
+
+This project is governed by the BSD 2-clause license. For details see the file
+titled LICENSE in the project root folder.
+
+Overview
+--------
+There are two separate libraries in the project, bsdiff and bspatch. Each are
+self contained in bsdiff.c and bspatch.c The easiest way to integrate is to
+simply copy the c file to your source folder and build it.
+
+The overarching goal was to modify the original bsdiff/bspatch code from Colin
+and eliminate external dependencies and provide a simple interface to the core
+functionality.
+
+I've exposed relevant functions via the `_stream` classes. The only external
+dependency not exposed is `memcmp` in `bsdiff`.
+
+This library generates patches that are not compatible with the original bsdiff
+tool. The impompatibilities were motivated by the patching needs for the game
+AirMech <https://www.carbongames.com> and the following requirements:
+
+* Eliminate/minimize any seek operations when applying patches
+* Eliminate any required disk I/O and support embedded streams
+* Ability to easily embed the routines as a library instead of an external binary
+* Compile+run on all platforms we use to build the game (Windows, Linux, NaCl, OSX)
+
+Compiling
+---------
+The libraries should compile warning free in any moderately recent version of
+gcc. The project uses `<stdint.h>` which is technically a C99 file and not
+available in Microsoft Visual Studio. The easiest solution here is to use the
+msinttypes version of stdint.h from <https://code.google.com/p/msinttypes/>.
+The direct link for the lazy people is:
+<https://msinttypes.googlecode.com/svn/trunk/stdint.h>.
+
+If your compiler does not provide an implementation of `<stdint.h>` you can
+remove the header from the bsdiff/bspatch files and provide your own typedefs
+for the following symbols: `uint8_t`, `uint64_t` and `int64_t`.
+
+Examples
+--------
+Each project has an optional main function that serves as an example for using
+the library. Simply defined `BSDIFF_EXECUTABLE` or `BSPATCH_EXECUTABLE` to
+enable building the standalone tools.
+
+Reference
+---------
+### bsdiff
+
+ struct bsdiff_stream
+ {
+ void* opaque;
+ void* (*malloc)(size_t size);
+ void (*free)(void* ptr);
+ int (*write)(struct bsdiff_stream* stream,
+ const void* buffer, int size);
+ };
+
+ int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new,
+ int64_t newsize, struct bsdiff_stream* stream);
+
+
+In order to use `bsdiff`, you need to define functions for allocating memory and
+writing binary data. This behavior is controlled by the `stream` parameted
+passed to to `bsdiff(...)`.
+
+The `opaque` field is never read or modified from within the `bsdiff` function.
+The caller can use this field to store custom state data needed for the callback
+functions.
+
+The `malloc` and `free` members should point to functions that behave like the
+standard `malloc` and `free` C functions.
+
+The `write` function is called by bsdiff to write a block of binary data to the
+stream. The return value for `write` should be `0` on success and non-zero if
+the callback failed to write all data. In the default example, bzip2 is used to
+compress output data.
+
+`bsdiff` returns `0` on success and `-1` on failure.
+
+### bspatch
+
+ struct bspatch_stream
+ {
+ void* opaque;
+ int (*read)(const struct bspatch_stream* stream,
+ void* buffer, int length);
+ };
+
+ int bspatch(const uint8_t* old, int64_t oldsize, uint8_t* new,
+ int64_t newsize, struct bspatch_stream* stream);
+
+The `bspatch` function transforms the data for a file using data generated from
+`bsdiff`. The caller takes care of loading the old file and allocating space for
+new file data. The `stream` parameter controls the process for reading binary
+patch data.
+
+The `opaque` field is never read or modified from within the bspatch function.
+The caller can use this field to store custom state data needed for the read
+function.
+
+The `read` function is called by `bspatch` to read a block of binary data from
+the stream. The return value for `read` should be `0` on success and non-zero
+if the callback failed to read the requested amount of data. In the default
+example, bzip2 is used to decompress input data.
+
+`bspatch` returns `0` on success and `-1` on failure. On success, `new` contains
+the data for the patched file.
--- /dev/null
+#!/bin/sh
+
+touch AUTHORS NEWS README ChangeLog
+cp LICENSE COPYING
+
+autoreconf -fis
--- /dev/null
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * Copyright 2012 Matthew Endsley
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bsdiff.h"
+
+#include <limits.h>
+#include <string.h>
+
+#define MIN(x,y) (((x)<(y)) ? (x) : (y))
+
+static void split(int64_t *I,int64_t *V,int64_t start,int64_t len,int64_t h)
+{
+ int64_t i,j,k,x,tmp,jj,kk;
+
+ if(len<16) {
+ for(k=start;k<start+len;k+=j) {
+ j=1;x=V[I[k]+h];
+ for(i=1;k+i<start+len;i++) {
+ if(V[I[k+i]+h]<x) {
+ x=V[I[k+i]+h];
+ j=0;
+ };
+ if(V[I[k+i]+h]==x) {
+ tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
+ j++;
+ };
+ };
+ for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
+ if(j==1) I[k]=-1;
+ };
+ return;
+ };
+
+ x=V[I[start+len/2]+h];
+ jj=0;kk=0;
+ for(i=start;i<start+len;i++) {
+ if(V[I[i]+h]<x) jj++;
+ if(V[I[i]+h]==x) kk++;
+ };
+ jj+=start;kk+=jj;
+
+ i=start;j=0;k=0;
+ while(i<jj) {
+ if(V[I[i]+h]<x) {
+ i++;
+ } else if(V[I[i]+h]==x) {
+ tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
+ j++;
+ } else {
+ tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
+ k++;
+ };
+ };
+
+ while(jj+j<kk) {
+ if(V[I[jj+j]+h]==x) {
+ j++;
+ } else {
+ tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
+ k++;
+ };
+ };
+
+ if(jj>start) split(I,V,start,jj-start,h);
+
+ for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
+ if(jj==kk-1) I[jj]=-1;
+
+ if(start+len>kk) split(I,V,kk,start+len-kk,h);
+}
+
+static void qsufsort(int64_t *I,int64_t *V,const uint8_t *old,int64_t oldsize)
+{
+ int64_t buckets[256];
+ int64_t i,h,len;
+
+ for(i=0;i<256;i++) buckets[i]=0;
+ for(i=0;i<oldsize;i++) buckets[old[i]]++;
+ for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
+ for(i=255;i>0;i--) buckets[i]=buckets[i-1];
+ buckets[0]=0;
+
+ for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
+ I[0]=oldsize;
+ for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
+ V[oldsize]=0;
+ for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
+ I[0]=-1;
+
+ for(h=1;I[0]!=-(oldsize+1);h+=h) {
+ len=0;
+ for(i=0;i<oldsize+1;) {
+ if(I[i]<0) {
+ len-=I[i];
+ i-=I[i];
+ } else {
+ if(len) I[i-len]=-len;
+ len=V[I[i]]+1-i;
+ split(I,V,i,len,h);
+ i+=len;
+ len=0;
+ };
+ };
+ if(len) I[i-len]=-len;
+ };
+
+ for(i=0;i<oldsize+1;i++) I[V[i]]=i;
+}
+
+static int64_t matchlen(const uint8_t *old,int64_t oldsize,const uint8_t *new,int64_t newsize)
+{
+ int64_t i;
+
+ for(i=0;(i<oldsize)&&(i<newsize);i++)
+ if(old[i]!=new[i]) break;
+
+ return i;
+}
+
+static int64_t search(const int64_t *I,const uint8_t *old,int64_t oldsize,
+ const uint8_t *new,int64_t newsize,int64_t st,int64_t en,int64_t *pos)
+{
+ int64_t x,y;
+
+ if(en-st<2) {
+ x=matchlen(old+I[st],oldsize-I[st],new,newsize);
+ y=matchlen(old+I[en],oldsize-I[en],new,newsize);
+
+ if(x>y) {
+ *pos=I[st];
+ return x;
+ } else {
+ *pos=I[en];
+ return y;
+ }
+ };
+
+ x=st+(en-st)/2;
+ if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) {
+ return search(I,old,oldsize,new,newsize,x,en,pos);
+ } else {
+ return search(I,old,oldsize,new,newsize,st,x,pos);
+ };
+}
+
+static void offtout(int64_t x,uint8_t *buf)
+{
+ int64_t y;
+
+ if(x<0) y=-x; else y=x;
+
+ buf[0]=y%256;y-=buf[0];
+ y=y/256;buf[1]=y%256;y-=buf[1];
+ y=y/256;buf[2]=y%256;y-=buf[2];
+ y=y/256;buf[3]=y%256;y-=buf[3];
+ y=y/256;buf[4]=y%256;y-=buf[4];
+ y=y/256;buf[5]=y%256;y-=buf[5];
+ y=y/256;buf[6]=y%256;y-=buf[6];
+ y=y/256;buf[7]=y%256;
+
+ if(x<0) buf[7]|=0x80;
+}
+
+static int64_t writedata(struct bsdiff_stream* stream, const void* buffer, int64_t length)
+{
+ int64_t result = 0;
+
+ while (length > 0)
+ {
+ const int smallsize = (int)MIN(length, INT_MAX);
+ const int writeresult = stream->write(stream, buffer, smallsize);
+ if (writeresult == -1)
+ {
+ return -1;
+ }
+
+ result += writeresult;
+ length -= smallsize;
+ buffer = (uint8_t*)buffer + smallsize;
+ }
+
+ return result;
+}
+
+struct bsdiff_request
+{
+ const uint8_t* old;
+ int64_t oldsize;
+ const uint8_t* new;
+ int64_t newsize;
+ struct bsdiff_stream* stream;
+ int64_t *I;
+ uint8_t *buffer;
+};
+
+static int bsdiff_internal(const struct bsdiff_request req)
+{
+ int64_t *I,*V;
+ int64_t scan,pos,len;
+ int64_t lastscan,lastpos,lastoffset;
+ int64_t oldscore,scsc;
+ int64_t s,Sf,lenf,Sb,lenb;
+ int64_t overlap,Ss,lens;
+ int64_t i;
+ uint8_t *buffer;
+ uint8_t buf[8 * 3];
+
+ if((V=req.stream->malloc((req.oldsize+1)*sizeof(int64_t)))==NULL) return -1;
+ I = req.I;
+
+ qsufsort(I,V,req.old,req.oldsize);
+ req.stream->free(V);
+
+ buffer = req.buffer;
+
+ /* Compute the differences, writing ctrl as we go */
+ scan=0;len=0;pos=0;
+ lastscan=0;lastpos=0;lastoffset=0;
+ while(scan<req.newsize) {
+ oldscore=0;
+
+ for(scsc=scan+=len;scan<req.newsize;scan++) {
+ len=search(I,req.old,req.oldsize,req.new+scan,req.newsize-scan,
+ 0,req.oldsize,&pos);
+
+ for(;scsc<scan+len;scsc++)
+ if((scsc+lastoffset<req.oldsize) &&
+ (req.old[scsc+lastoffset] == req.new[scsc]))
+ oldscore++;
+
+ if(((len==oldscore) && (len!=0)) ||
+ (len>oldscore+8)) break;
+
+ if((scan+lastoffset<req.oldsize) &&
+ (req.old[scan+lastoffset] == req.new[scan]))
+ oldscore--;
+ };
+
+ if((len!=oldscore) || (scan==req.newsize)) {
+ s=0;Sf=0;lenf=0;
+ for(i=0;(lastscan+i<scan)&&(lastpos+i<req.oldsize);) {
+ if(req.old[lastpos+i]==req.new[lastscan+i]) s++;
+ i++;
+ if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
+ };
+
+ lenb=0;
+ if(scan<req.newsize) {
+ s=0;Sb=0;
+ for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
+ if(req.old[pos-i]==req.new[scan-i]) s++;
+ if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
+ };
+ };
+
+ if(lastscan+lenf>scan-lenb) {
+ overlap=(lastscan+lenf)-(scan-lenb);
+ s=0;Ss=0;lens=0;
+ for(i=0;i<overlap;i++) {
+ if(req.new[lastscan+lenf-overlap+i]==
+ req.old[lastpos+lenf-overlap+i]) s++;
+ if(req.new[scan-lenb+i]==
+ req.old[pos-lenb+i]) s--;
+ if(s>Ss) { Ss=s; lens=i+1; };
+ };
+
+ lenf+=lens-overlap;
+ lenb-=lens;
+ };
+
+ offtout(lenf,buf);
+ offtout((scan-lenb)-(lastscan+lenf),buf+8);
+ offtout((pos-lenb)-(lastpos+lenf),buf+16);
+
+ /* Write control data */
+ if (writedata(req.stream, buf, sizeof(buf)))
+ return -1;
+
+ /* Write diff data */
+ for(i=0;i<lenf;i++)
+ buffer[i]=req.new[lastscan+i]-req.old[lastpos+i];
+ if (writedata(req.stream, buffer, lenf))
+ return -1;
+
+ /* Write extra data */
+ for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
+ buffer[i]=req.new[lastscan+lenf+i];
+ if (writedata(req.stream, buffer, (scan-lenb)-(lastscan+lenf)))
+ return -1;
+
+ lastscan=scan-lenb;
+ lastpos=pos-lenb;
+ lastoffset=pos-scan;
+ };
+ };
+
+ return 0;
+}
+
+int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new, int64_t newsize, struct bsdiff_stream* stream)
+{
+ int result;
+ struct bsdiff_request req;
+
+ if((req.I=stream->malloc((oldsize+1)*sizeof(int64_t)))==NULL)
+ return -1;
+
+ if((req.buffer=stream->malloc(newsize+1))==NULL)
+ {
+ stream->free(req.I);
+ return -1;
+ }
+
+ req.old = old;
+ req.oldsize = oldsize;
+ req.new = new;
+ req.newsize = newsize;
+ req.stream = stream;
+
+ result = bsdiff_internal(req);
+
+ stream->free(req.buffer);
+ stream->free(req.I);
+
+ return result;
+}
+
+#if defined(BSDIFF_EXECUTABLE)
+
+#include <sys/types.h>
+
+#include <bzlib.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static int bz2_write(struct bsdiff_stream* stream, const void* buffer, int size)
+{
+ int bz2err;
+ BZFILE* bz2;
+
+ bz2 = (BZFILE*)stream->opaque;
+ BZ2_bzWrite(&bz2err, bz2, (void*)buffer, size);
+ if (bz2err != BZ_STREAM_END && bz2err != BZ_OK)
+ return -1;
+
+ return 0;
+}
+
+int main(int argc,char *argv[])
+{
+ int fd;
+ int bz2err;
+ uint8_t *old,*new;
+ off_t oldsize,newsize;
+ uint8_t buf[8];
+ FILE * pf;
+ struct bsdiff_stream stream;
+ BZFILE* bz2;
+
+ memset(&bz2, 0, sizeof(bz2));
+ stream.malloc = malloc;
+ stream.free = free;
+ stream.write = bz2_write;
+
+ if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
+
+ /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure
+ that we never try to malloc(0) and get a NULL pointer */
+ if(((fd=open(argv[1],O_RDONLY,0))<0) ||
+ ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
+ ((old=malloc(oldsize+1))==NULL) ||
+ (lseek(fd,0,SEEK_SET)!=0) ||
+ (read(fd,old,oldsize)!=oldsize) ||
+ (close(fd)==-1)) err(1,"%s",argv[1]);
+
+
+ /* Allocate newsize+1 bytes instead of newsize bytes to ensure
+ that we never try to malloc(0) and get a NULL pointer */
+ if(((fd=open(argv[2],O_RDONLY,0))<0) ||
+ ((newsize=lseek(fd,0,SEEK_END))==-1) ||
+ ((new=malloc(newsize+1))==NULL) ||
+ (lseek(fd,0,SEEK_SET)!=0) ||
+ (read(fd,new,newsize)!=newsize) ||
+ (close(fd)==-1)) err(1,"%s",argv[2]);
+
+ /* Create the patch file */
+ if ((pf = fopen(argv[3], "w")) == NULL)
+ err(1, "%s", argv[3]);
+
+ /* Write header (signature+newsize)*/
+ offtout(newsize, buf);
+ if (fwrite("ENDSLEY/BSDIFF43", 16, 1, pf) != 1 ||
+ fwrite(buf, sizeof(buf), 1, pf) != 1)
+ err(1, "Failed to write header");
+
+
+ if (NULL == (bz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)))
+ errx(1, "BZ2_bzWriteOpen, bz2err=%d", bz2err);
+
+ stream.opaque = bz2;
+ if (bsdiff(old, oldsize, new, newsize, &stream))
+ err(1, "bsdiff");
+
+ BZ2_bzWriteClose(&bz2err, bz2, 0, NULL, NULL);
+ if (bz2err != BZ_OK)
+ err(1, "BZ2_bzWriteClose, bz2err=%d", bz2err);
+
+ if (fclose(pf))
+ err(1, "fclose");
+
+ /* Free the memory we used */
+ free(old);
+ free(new);
+
+ return 0;
+}
+
+#endif
--- /dev/null
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * Copyright 2012 Matthew Endsley
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSDIFF_H
+# define BSDIFF_H
+
+# include <stddef.h>
+# include <stdint.h>
+
+struct bsdiff_stream
+{
+ void* opaque;
+
+ void* (*malloc)(size_t size);
+ void (*free)(void* ptr);
+ int (*write)(struct bsdiff_stream* stream, const void* buffer, int size);
+};
+
+int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new, int64_t newsize, struct bsdiff_stream* stream);
+
+#endif
--- /dev/null
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * Copyright 2012 Matthew Endsley
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bspatch.h"
+
+static int64_t offtin(uint8_t *buf)
+{
+ int64_t y;
+
+ y=buf[7]&0x7F;
+ y=y*256;y+=buf[6];
+ y=y*256;y+=buf[5];
+ y=y*256;y+=buf[4];
+ y=y*256;y+=buf[3];
+ y=y*256;y+=buf[2];
+ y=y*256;y+=buf[1];
+ y=y*256;y+=buf[0];
+
+ if(buf[7]&0x80) y=-y;
+
+ return y;
+}
+
+int bspatch(const uint8_t* old, int64_t oldsize, uint8_t* new, int64_t newsize, struct bspatch_stream* stream)
+{
+ uint8_t buf[8];
+ int64_t oldpos,newpos;
+ int64_t ctrl[3];
+ int64_t i;
+
+ oldpos=0;newpos=0;
+ while(newpos<newsize) {
+ /* Read control data */
+ for(i=0;i<=2;i++) {
+ if (stream->read(stream, buf, 8))
+ return -1;
+ ctrl[i]=offtin(buf);
+ };
+
+ /* Sanity-check */
+ if(newpos+ctrl[0]>newsize)
+ return -1;
+
+ /* Read diff string */
+ if (stream->read(stream, new + newpos, ctrl[0]))
+ return -1;
+
+ /* Add old data to diff string */
+ for(i=0;i<ctrl[0];i++)
+ if((oldpos+i>=0) && (oldpos+i<oldsize))
+ new[newpos+i]+=old[oldpos+i];
+
+ /* Adjust pointers */
+ newpos+=ctrl[0];
+ oldpos+=ctrl[0];
+
+ /* Sanity-check */
+ if(newpos+ctrl[1]>newsize)
+ return -1;
+
+ /* Read extra string */
+ if (stream->read(stream, new + newpos, ctrl[1]))
+ return -1;
+
+ /* Adjust pointers */
+ newpos+=ctrl[1];
+ oldpos+=ctrl[2];
+ };
+
+ return 0;
+}
+
+#if defined(BSPATCH_EXECUTABLE)
+
+#include <bzlib.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static int bz2_read(const struct bspatch_stream* stream, void* buffer, int length)
+{
+ int n;
+ int bz2err;
+ BZFILE* bz2;
+
+ bz2 = (BZFILE*)stream->opaque;
+ n = BZ2_bzRead(&bz2err, bz2, buffer, length);
+ if (n != length)
+ return -1;
+
+ return 0;
+}
+
+int main(int argc,char * argv[])
+{
+ FILE * f;
+ int fd;
+ int bz2err;
+ uint8_t header[24];
+ uint8_t *old, *new;
+ int64_t oldsize, newsize;
+ BZFILE* bz2;
+ struct bspatch_stream stream;
+
+ if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
+
+ /* Open patch file */
+ if ((f = fopen(argv[3], "r")) == NULL)
+ err(1, "fopen(%s)", argv[3]);
+
+ /* Read header */
+ if (fread(header, 1, 24, f) != 24) {
+ if (feof(f))
+ errx(1, "Corrupt patch\n");
+ err(1, "fread(%s)", argv[3]);
+ }
+
+ /* Check for appropriate magic */
+ if (memcmp(header, "ENDSLEY/BSDIFF43", 16) != 0)
+ errx(1, "Corrupt patch\n");
+
+ /* Read lengths from header */
+ newsize=offtin(header+16);
+ if(newsize<0)
+ errx(1,"Corrupt patch\n");
+
+ /* Close patch file and re-open it via libbzip2 at the right places */
+ if(((fd=open(argv[1],O_RDONLY,0))<0) ||
+ ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
+ ((old=malloc(oldsize+1))==NULL) ||
+ (lseek(fd,0,SEEK_SET)!=0) ||
+ (read(fd,old,oldsize)!=oldsize) ||
+ (close(fd)==-1)) err(1,"%s",argv[1]);
+ if((new=malloc(newsize+1))==NULL) err(1,NULL);
+
+ if (NULL == (bz2 = BZ2_bzReadOpen(&bz2err, f, 0, 0, NULL, 0)))
+ errx(1, "BZ2_bzReadOpen, bz2err=%d", bz2err);
+
+ stream.read = bz2_read;
+ stream.opaque = bz2;
+ if (bspatch(old, oldsize, new, newsize, &stream))
+ errx(1, "bspatch");
+
+ /* Clean up the bzip2 reads */
+ BZ2_bzReadClose(&bz2err, bz2);
+ fclose(f);
+
+ /* Write the new file */
+ if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||
+ (write(fd,new,newsize)!=newsize) || (close(fd)==-1))
+ err(1,"%s",argv[2]);
+
+ free(new);
+ free(old);
+
+ return 0;
+}
+
+#endif
--- /dev/null
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * Copyright 2012 Matthew Endsley
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPATCH_H
+# define BSPATCH_H
+
+# include <stdint.h>
+
+struct bspatch_stream
+{
+ void* opaque;
+ int (*read)(const struct bspatch_stream* stream, void* buffer, int length);
+};
+
+int bspatch(const uint8_t* old, int64_t oldsize, uint8_t* new, int64_t newsize, struct bspatch_stream* stream);
+
+#endif
+
--- /dev/null
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.69])
+AC_INIT([bsdiff], [0.1])
+AC_CONFIG_SRCDIR([bsdiff.c])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([1.9])
+
+# Checks for programs.
+AC_PROG_CC
+
+# Checks for libraries.
+# FIXME: Replace `main' with a function in `-lbz2':
+AC_CHECK_LIB([bz2], [BZ2_bzReadOpen])
+
+AC_CHECK_HEADERS([fcntl.h limits.h stddef.h stdint.h stdlib.h string.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_INT64_T
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_CHECK_FUNCS([memset])
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+++ /dev/null
-Subproject commit 03138641298fd6799f46b16423871f959332bacf
--- /dev/null
+# A path ostree writes to work around automake bug with
+# subdir-objects
+Makefile-libglnx.am.inc
+
+# Some standard bits
+.deps
+.libs
+.dirstamp
+*.typelib
+*.la
+*.lo
+*.o
+*.pyc
+*.stamp
+*~
+
--- /dev/null
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+# Copyright (C) 2015 Colin Walters <walters@verbum.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+EXTRA_DIST += $(libglnx_srcpath)/README.md $(libglnx_srcpath)/COPYING
+
+libglnx_la_SOURCES = \
+ $(libglnx_srcpath)/glnx-backport-autocleanups.h \
+ $(libglnx_srcpath)/glnx-backport-autoptr.h \
+ $(libglnx_srcpath)/glnx-backports.h \
+ $(libglnx_srcpath)/glnx-backports.c \
+ $(libglnx_srcpath)/glnx-local-alloc.h \
+ $(libglnx_srcpath)/glnx-local-alloc.c \
+ $(libglnx_srcpath)/glnx-errors.h \
+ $(libglnx_srcpath)/glnx-errors.c \
+ $(libglnx_srcpath)/glnx-console.h \
+ $(libglnx_srcpath)/glnx-console.c \
+ $(libglnx_srcpath)/glnx-dirfd.h \
+ $(libglnx_srcpath)/glnx-dirfd.c \
+ $(libglnx_srcpath)/glnx-fdio.h \
+ $(libglnx_srcpath)/glnx-fdio.c \
+ $(libglnx_srcpath)/glnx-lockfile.h \
+ $(libglnx_srcpath)/glnx-lockfile.c \
+ $(libglnx_srcpath)/glnx-libcontainer.h \
+ $(libglnx_srcpath)/glnx-libcontainer.c \
+ $(libglnx_srcpath)/glnx-xattrs.h \
+ $(libglnx_srcpath)/glnx-xattrs.c \
+ $(libglnx_srcpath)/glnx-shutil.h \
+ $(libglnx_srcpath)/glnx-shutil.c \
+ $(libglnx_srcpath)/libglnx.h \
+ $(NULL)
+
+libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags)
+libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic
+libglnx_la_LIBADD = $(libglnx_libs)
--- /dev/null
+libglnx is the successor to libgsystem: https://git.gnome.org/browse/libgsystem
+
+It is for modules which depend on both GLib and Linux, intended to be
+used as a git submodule.
+
+Features:
+
+ - File APIs which use `openat()` like APIs, but also take a `GCancellable`
+ to support dynamic cancellation
+ - APIs also have a `GError` parameter
+ - High level "shutil", somewhat inspired by Python's
+ - A "console" API for tty output
+ - Some basic container utility functions
+ - A backport of the GLib cleanup macros for projects which can't yet take
+ a dependency on 2.40.
+
+Why?
+----
+
+There are multiple projects which have a hard dependency on Linux and
+GLib, such as NetworkManager, ostree, xdg-app, etc. It makes sense
+for them to be able to share Linux-specific APIs.
+
+This module also contains some code taken from systemd, which has very
+high quality LGPLv2+ shared library code, but most of the internal
+shared library is private, and not namespaced.
+
+One could also compare this project to gnulib; the salient differences
+there are that at least some of this module is eventually destined for
+inclusion in GLib.
+
+Porting from libgsystem
+-----------------------
+
+For all of the filesystem access code, libglnx exposes only
+fd-relative API, not `GFile*`. It does use `GCancellable` where
+applicable.
+
+For local allocation macros, you should start using the `g_auto`
+macros from GLib. A backport is included in libglnx. There are a few
+APIs not defined in GLib yet, such as `glnx_fd_close`.
+
+`gs_transfer_out_value` is replaced by `g_steal_pointer`.
+
+Contributing
+------------
+
+Currently there is not a Bugzilla product - one may be created
+in the future. You can submit PRs against the Github mirror:
+
+https://github.com/GNOME/libglnx/pulls
+
+Or alternatively, email one of the maintainers directly.
--- /dev/null
+/*
+ * Copyright © 2015 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#pragma once
+
+#include <glnx-backport-autoptr.h>
+
+#if !GLIB_CHECK_VERSION(2, 43, 4)
+
+static inline void
+g_autoptr_cleanup_generic_gfree (void *p)
+{
+ void **pp = (void**)p;
+ if (*pp)
+ g_free (*pp);
+}
+
+static inline void
+g_autoptr_cleanup_gstring_free (GString *string)
+{
+ if (string)
+ g_string_free (string, TRUE);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncQueue, g_async_queue_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBookmarkFile, g_bookmark_file_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBytes, g_bytes_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GChecksum, g_checksum_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDateTime, g_date_time_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDir, g_dir_close)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GError, g_error_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHashTable, g_hash_table_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHmac, g_hmac_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GIOChannel, g_io_channel_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GKeyFile, g_key_file_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GList, g_list_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GArray, g_array_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPtrArray, g_ptr_array_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContext, g_main_context_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainLoop, g_main_loop_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSource, g_source_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GString, g_autoptr_cleanup_gstring_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMappedFile, g_mapped_file_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMarkupParseContext, g_markup_parse_context_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, g_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GNode, g_node_destroy)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionContext, g_option_context_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionGroup, g_option_group_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPatternSpec, g_pattern_spec_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GQueue, g_queue_free)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GQueue, g_queue_clear)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRand, g_rand_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRegex, g_regex_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMatchInfo, g_match_info_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GScanner, g_scanner_destroy)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSequence, g_sequence_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSList, g_slist_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GStringChunk, g_string_chunk_free)
+G_DEFINE_AUTO_CLEANUP_FREE_FUNC(GStrv, g_strfreev, NULL)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GThread, g_thread_unref)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GMutex, g_mutex_clear)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GCond, g_cond_clear)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimer, g_timer_destroy)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimeZone, g_time_zone_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTree, g_tree_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariant, g_variant_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantBuilder, g_variant_builder_unref)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantBuilder, g_variant_builder_clear)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantIter, g_variant_iter_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantDict, g_variant_dict_unref)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocess, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocessLauncher, g_object_unref)
+
+/* Add GObject-based types as needed. */
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCancellable, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverter, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterOutputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDataInputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFile, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileEnumerator, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileIOStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInfo, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileMonitor, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileOutputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryInputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryOutputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOutputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocket, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocketAddress, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTask, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsCertificate, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsDatabase, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsInteraction, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusConnection, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusMessage, g_object_unref)
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2015 Colin Walters <walters@verbum.org>
+ *
+ * GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#if !GLIB_CHECK_VERSION(2, 43, 4)
+
+#define _GLIB_AUTOPTR_FUNC_NAME(TypeName) glib_autoptr_cleanup_##TypeName
+#define _GLIB_AUTOPTR_TYPENAME(TypeName) TypeName##_autoptr
+#define _GLIB_AUTO_FUNC_NAME(TypeName) glib_auto_cleanup_##TypeName
+#define _GLIB_CLEANUP(func) __attribute__((cleanup(func)))
+#define _GLIB_DEFINE_AUTOPTR_CHAINUP(ModuleObjName, ParentName) \
+ typedef ModuleObjName *_GLIB_AUTOPTR_TYPENAME(ModuleObjName); \
+ static inline void _GLIB_AUTOPTR_FUNC_NAME(ModuleObjName) (ModuleObjName **_ptr) { \
+ _GLIB_AUTOPTR_FUNC_NAME(ParentName) ((ParentName **) _ptr); } \
+
+
+/* these macros are API */
+#define G_DEFINE_AUTOPTR_CLEANUP_FUNC(TypeName, func) \
+ typedef TypeName *_GLIB_AUTOPTR_TYPENAME(TypeName); \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ static inline void _GLIB_AUTOPTR_FUNC_NAME(TypeName) (TypeName **_ptr) { if (*_ptr) (func) (*_ptr); } \
+ G_GNUC_END_IGNORE_DEPRECATIONS
+#define G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(TypeName, func) \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ static inline void _GLIB_AUTO_FUNC_NAME(TypeName) (TypeName *_ptr) { (func) (_ptr); } \
+ G_GNUC_END_IGNORE_DEPRECATIONS
+#define G_DEFINE_AUTO_CLEANUP_FREE_FUNC(TypeName, func, none) \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ static inline void _GLIB_AUTO_FUNC_NAME(TypeName) (TypeName *_ptr) { if (*_ptr != none) (func) (*_ptr); } \
+ G_GNUC_END_IGNORE_DEPRECATIONS
+#define g_autoptr(TypeName) _GLIB_CLEANUP(_GLIB_AUTOPTR_FUNC_NAME(TypeName)) _GLIB_AUTOPTR_TYPENAME(TypeName)
+#define g_auto(TypeName) _GLIB_CLEANUP(_GLIB_AUTO_FUNC_NAME(TypeName)) TypeName
+#define g_autofree _GLIB_CLEANUP(g_autoptr_cleanup_generic_gfree)
+
+/**
+ * g_steal_pointer:
+ * @pp: a pointer to a pointer
+ *
+ * Sets @pp to %NULL, returning the value that was there before.
+ *
+ * Conceptually, this transfers the ownership of the pointer from the
+ * referenced variable to the "caller" of the macro (ie: "steals" the
+ * reference).
+ *
+ * The return value will be properly typed, according to the type of
+ * @pp.
+ *
+ * This can be very useful when combined with g_autoptr() to prevent the
+ * return value of a function from being automatically freed. Consider
+ * the following example (which only works on GCC and clang):
+ *
+ * |[
+ * GObject *
+ * create_object (void)
+ * {
+ * g_autoptr(GObject) obj = g_object_new (G_TYPE_OBJECT, NULL);
+ *
+ * if (early_error_case)
+ * return NULL;
+ *
+ * return g_steal_pointer (&obj);
+ * }
+ * ]|
+ *
+ * It can also be used in similar ways for 'out' parameters and is
+ * particularly useful for dealing with optional out parameters:
+ *
+ * |[
+ * gboolean
+ * get_object (GObject **obj_out)
+ * {
+ * g_autoptr(GObject) obj = g_object_new (G_TYPE_OBJECT, NULL);
+ *
+ * if (early_error_case)
+ * return FALSE;
+ *
+ * if (obj_out)
+ * *obj_out = g_steal_pointer (&obj);
+ *
+ * return TRUE;
+ * }
+ * ]|
+ *
+ * In the above example, the object will be automatically freed in the
+ * early error case and also in the case that %NULL was given for
+ * @obj_out.
+ *
+ * Since: 2.44
+ */
+static inline gpointer
+(g_steal_pointer) (gpointer pp)
+{
+ gpointer *ptr = (gpointer *) pp;
+ gpointer ref;
+
+ ref = *ptr;
+ *ptr = NULL;
+
+ return ref;
+}
+
+/* type safety */
+#define g_steal_pointer(pp) \
+ (0 ? (*(pp)) : (g_steal_pointer) (pp))
+
+#endif /* !GLIB_CHECK_VERSION(2, 43, 3) */
+
+G_END_DECLS
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2015 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "glnx-backports.h"
+
+#if !GLIB_CHECK_VERSION(2, 44, 0)
+gboolean
+glnx_strv_contains (const gchar * const *strv,
+ const gchar *str)
+{
+ g_return_val_if_fail (strv != NULL, FALSE);
+ g_return_val_if_fail (str != NULL, FALSE);
+
+ for (; *strv != NULL; strv++)
+ {
+ if (g_str_equal (str, *strv))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+glnx_set_object (GObject **object_ptr,
+ GObject *new_object)
+{
+ GObject *old_object = *object_ptr;
+
+ if (old_object == new_object)
+ return FALSE;
+
+ if (new_object != NULL)
+ g_object_ref (new_object);
+
+ *object_ptr = new_object;
+
+ if (old_object != NULL)
+ g_object_unref (old_object);
+
+ return TRUE;
+}
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2015 Colin Walters <walters@verbum.org>
+ *
+ * GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#if !GLIB_CHECK_VERSION(2, 44, 0)
+
+#define g_strv_contains glnx_strv_contains
+gboolean glnx_strv_contains (const gchar * const *strv,
+ const gchar *str);
+
+#define g_set_object(object_ptr, new_object) \
+ (/* Check types match. */ \
+ 0 ? *(object_ptr) = (new_object), FALSE : \
+ glnx_set_object ((GObject **) (object_ptr), (GObject *) (new_object)) \
+ )
+gboolean glnx_set_object (GObject **object_ptr,
+ GObject *new_object);
+
+#endif /* !GLIB_CHECK_VERSION(2, 44, 0) */
+
+G_END_DECLS
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013,2014,2015 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "glnx-console.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+static char *current_text = NULL;
+static gint current_percent = -1;
+static gboolean locked;
+
+static gboolean
+stdout_is_tty (void)
+{
+ static gsize initialized = 0;
+ static gboolean stdout_is_tty_v;
+
+ if (g_once_init_enter (&initialized))
+ {
+ stdout_is_tty_v = isatty (1);
+ g_once_init_leave (&initialized, 1);
+ }
+
+ return stdout_is_tty_v;
+}
+
+static volatile guint cached_columns = 0;
+static volatile guint cached_lines = 0;
+
+static int
+fd_columns (int fd)
+{
+ struct winsize ws = {};
+
+ if (ioctl (fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_col <= 0)
+ return -EIO;
+
+ return ws.ws_col;
+}
+
+/**
+ * glnx_console_columns:
+ *
+ * Returns: The number of columns for terminal output
+ */
+guint
+glnx_console_columns (void)
+{
+ if (G_UNLIKELY (cached_columns == 0))
+ {
+ int c;
+
+ c = fd_columns (STDOUT_FILENO);
+
+ if (c <= 0)
+ c = 80;
+
+ if (c > 256)
+ c = 256;
+
+ cached_columns = c;
+ }
+
+ return cached_columns;
+}
+
+static int
+fd_lines (int fd)
+{
+ struct winsize ws = {};
+
+ if (ioctl (fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_row <= 0)
+ return -EIO;
+
+ return ws.ws_row;
+}
+
+/**
+ * glnx_console_lines:
+ *
+ * Returns: The number of lines for terminal output
+ */
+guint
+glnx_console_lines (void)
+{
+ if (G_UNLIKELY (cached_lines == 0))
+ {
+ int l;
+
+ l = fd_lines (STDOUT_FILENO);
+
+ if (l <= 0)
+ l = 24;
+
+ cached_lines = l;
+ }
+
+ return cached_lines;
+}
+
+static void
+on_sigwinch (int signum)
+{
+ cached_columns = 0;
+ cached_lines = 0;
+}
+
+void
+glnx_console_lock (GLnxConsoleRef *console)
+{
+ static gsize sigwinch_initialized = 0;
+
+ g_return_if_fail (!locked);
+ g_return_if_fail (!console->locked);
+
+ console->is_tty = stdout_is_tty ();
+
+ locked = console->locked = TRUE;
+
+ current_percent = 0;
+
+ if (console->is_tty)
+ {
+ if (g_once_init_enter (&sigwinch_initialized))
+ {
+ signal (SIGWINCH, on_sigwinch);
+ g_once_init_leave (&sigwinch_initialized, 1);
+ }
+
+ { static const char initbuf[] = { '\n', 0x1B, 0x37 };
+ (void) fwrite (initbuf, 1, sizeof (initbuf), stdout);
+ }
+ }
+}
+
+static void
+printpad (const char *padbuf,
+ guint padbuf_len,
+ guint n)
+{
+ const guint d = n / padbuf_len;
+ const guint r = n % padbuf_len;
+ guint i;
+
+ for (i = 0; i < d; i++)
+ fwrite (padbuf, 1, padbuf_len, stdout);
+ fwrite (padbuf, 1, r, stdout);
+}
+
+/**
+ * glnx_console_progress_text_percent:
+ * @text: Show this text before the progress bar
+ * @percentage: An integer in the range of 0 to 100
+ *
+ * On a tty, print to the console @text followed by an ASCII art
+ * progress bar whose percentage is @percentage. If stdout is not a
+ * tty, a more basic line by line change will be printed.
+ *
+ * You must have called glnx_console_lock() before invoking this
+ * function.
+ *
+ */
+void
+glnx_console_progress_text_percent (const char *text,
+ guint percentage)
+{
+ static const char equals[] = "====================";
+ const guint n_equals = sizeof (equals) - 1;
+ static const char spaces[] = " ";
+ const guint n_spaces = sizeof (spaces) - 1;
+ const guint ncolumns = glnx_console_columns ();
+ const guint bar_min = 10;
+ const guint input_textlen = text ? strlen (text) : 0;
+ guint textlen;
+ guint barlen;
+
+ g_return_if_fail (percentage >= 0 && percentage <= 100);
+
+ if (text && !*text)
+ text = NULL;
+
+ if (percentage == current_percent
+ && g_strcmp0 (text, current_text) == 0)
+ return;
+
+ if (!stdout_is_tty ())
+ {
+ if (text)
+ fprintf (stdout, "%s", text);
+ if (percentage != -1)
+ {
+ if (text)
+ fputc (' ', stdout);
+ fprintf (stdout, "%u%%", percentage);
+ }
+ fputc ('\n', stdout);
+ fflush (stdout);
+ return;
+ }
+
+ if (ncolumns < bar_min)
+ return; /* TODO: spinner */
+
+ /* Restore cursor */
+ { const char beginbuf[2] = { 0x1B, 0x38 };
+ (void) fwrite (beginbuf, 1, sizeof (beginbuf), stdout);
+ }
+
+ textlen = MIN (input_textlen, ncolumns - bar_min);
+ barlen = ncolumns - textlen;
+
+ if (textlen > 0)
+ {
+ fwrite (text, 1, textlen - 1, stdout);
+ fputc (' ', stdout);
+ }
+
+ {
+ const guint nbraces = 2;
+ const guint textpercent_len = 5;
+ const guint bar_internal_len = barlen - nbraces - textpercent_len;
+ const guint eqlen = bar_internal_len * (percentage / 100.0);
+ const guint spacelen = bar_internal_len - eqlen;
+
+ fputc ('[', stdout);
+ printpad (equals, n_equals, eqlen);
+ printpad (spaces, n_spaces, spacelen);
+ fputc (']', stdout);
+ fprintf (stdout, " %3d%%", percentage);
+ }
+
+ { const guint spacelen = ncolumns - textlen - barlen;
+ printpad (spaces, n_spaces, spacelen);
+ }
+
+ fflush (stdout);
+}
+
+/**
+ * glnx_console_unlock:
+ *
+ * Print a newline, and reset all cached console progress state.
+ *
+ * This function does nothing if stdout is not a tty.
+ */
+void
+glnx_console_unlock (GLnxConsoleRef *console)
+{
+ g_return_if_fail (locked);
+ g_return_if_fail (console->locked);
+
+ current_percent = -1;
+ g_clear_pointer (¤t_text, g_free);
+
+ if (console->is_tty)
+ fputc ('\n', stdout);
+
+ locked = FALSE;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013,2014,2015 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-backport-autocleanups.h>
+
+G_BEGIN_DECLS
+
+struct GLnxConsoleRef {
+ gboolean locked;
+ gboolean is_tty;
+};
+
+typedef struct GLnxConsoleRef GLnxConsoleRef;
+
+void glnx_console_lock (GLnxConsoleRef *ref);
+
+void glnx_console_progress_text_percent (const char *text,
+ guint percentage);
+
+void glnx_console_unlock (GLnxConsoleRef *ref);
+
+guint glnx_console_lines (void);
+
+guint glnx_console_columns (void);
+
+static inline void
+glnx_console_ref_cleanup (GLnxConsoleRef *p)
+{
+ glnx_console_unlock (p);
+}
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxConsoleRef, glnx_console_ref_cleanup)
+
+G_END_DECLS
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glnx-dirfd.h>
+#include <glnx-errors.h>
+#include <glnx-local-alloc.h>
+
+/**
+ * glnx_opendirat_with_errno:
+ * @dfd: File descriptor for origin directory
+ * @name: Pathname, relative to @dfd
+ * @follow: Whether or not to follow symbolic links
+ *
+ * Use openat() to open a directory, using a standard set of flags.
+ * This function sets errno.
+ */
+int
+glnx_opendirat_with_errno (int dfd,
+ const char *path,
+ gboolean follow)
+{
+ int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY;
+ if (!follow)
+ flags |= O_NOFOLLOW;
+
+ dfd = glnx_dirfd_canonicalize (dfd);
+
+ return openat (dfd, path, flags);
+}
+
+/**
+ * glnx_opendirat:
+ * @dfd: File descriptor for origin directory
+ * @path: Pathname, relative to @dfd
+ * @follow: Whether or not to follow symbolic links
+ * @error: Error
+ *
+ * Use openat() to open a directory, using a standard set of flags.
+ */
+gboolean
+glnx_opendirat (int dfd,
+ const char *path,
+ gboolean follow,
+ int *out_fd,
+ GError **error)
+{
+ int ret = glnx_opendirat_with_errno (dfd, path, follow);
+ if (ret == -1)
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", "openat");
+ return FALSE;
+ }
+ *out_fd = ret;
+ return TRUE;
+}
+
+struct GLnxRealDirfdIterator
+{
+ gboolean initialized;
+ int fd;
+ DIR *d;
+};
+typedef struct GLnxRealDirfdIterator GLnxRealDirfdIterator;
+
+/**
+ * glnx_dirfd_iterator_init_at:
+ * @dfd: File descriptor, may be AT_FDCWD or -1
+ * @path: Path, may be relative to @df
+ * @follow: If %TRUE and the last component of @path is a symlink, follow it
+ * @out_dfd_iter: (out caller-allocates): A directory iterator, will be initialized
+ * @error: Error
+ *
+ * Initialize @out_dfd_iter from @dfd and @path.
+ */
+gboolean
+glnx_dirfd_iterator_init_at (int dfd,
+ const char *path,
+ gboolean follow,
+ GLnxDirFdIterator *out_dfd_iter,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ glnx_fd_close int fd = -1;
+
+ if (!glnx_opendirat (dfd, path, follow, &fd, error))
+ goto out;
+
+ if (!glnx_dirfd_iterator_init_take_fd (fd, out_dfd_iter, error))
+ goto out;
+ fd = -1; /* Transfer ownership */
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+/**
+ * glnx_dirfd_iterator_init_take_fd:
+ * @dfd: File descriptor - ownership is taken
+ * @dfd_iter: A directory iterator
+ * @error: Error
+ *
+ * Steal ownership of @dfd, using it to initialize @dfd_iter for
+ * iteration.
+ */
+gboolean
+glnx_dirfd_iterator_init_take_fd (int dfd,
+ GLnxDirFdIterator *dfd_iter,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
+ DIR *d = NULL;
+
+ d = fdopendir (dfd);
+ if (!d)
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", "fdopendir");
+ goto out;
+ }
+
+ real_dfd_iter->fd = dfd;
+ real_dfd_iter->d = d;
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+/**
+ * glnx_dirfd_iterator_next_dent:
+ * @dfd_iter: A directory iterator
+ * @out_dent: (out) (transfer none): Pointer to dirent; do not free
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Read the next value from @dfd_iter, causing @out_dent to be
+ * updated. If end of stream is reached, @out_dent will be set
+ * to %NULL, and %TRUE will be returned.
+ */
+gboolean
+glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter,
+ struct dirent **out_dent,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto out;
+
+ do
+ {
+ errno = 0;
+ *out_dent = readdir (real_dfd_iter->d);
+ if (*out_dent == NULL && errno != 0)
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", "fdopendir");
+ goto out;
+ }
+ } while (*out_dent &&
+ (strcmp ((*out_dent)->d_name, ".") == 0 ||
+ strcmp ((*out_dent)->d_name, "..") == 0));
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+/**
+ * glnx_dirfd_iterator_clear:
+ * @dfd_iter: Iterator, will be de-initialized
+ *
+ * Unset @dfd_iter, freeing any resources. If @dfd_iter is not
+ * initialized, do nothing.
+ */
+void
+glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter)
+{
+ GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
+ /* fd is owned by dfd_iter */
+ (void) closedir (real_dfd_iter->d);
+ real_dfd_iter->initialized = FALSE;
+}
+
+/**
+ * glnx_fdrel_abspath:
+ * @dfd: Directory fd
+ * @path: Path
+ *
+ * Turn a fd-relative pair into something that can be used for legacy
+ * APIs expecting absolute paths.
+ *
+ * This is Linux specific, and only valid inside this process (unless
+ * you set up the child process to have the exact same fd number, but
+ * don't try that).
+ */
+char *
+glnx_fdrel_abspath (int dfd,
+ const char *path)
+{
+ dfd = glnx_dirfd_canonicalize (dfd);
+ if (dfd == AT_FDCWD)
+ return g_strdup (path);
+ return g_strdup_printf ("/proc/self/fd/%d/%s", dfd, path);
+}
+
+/**
+ * glnx_mkdtempat:
+ * @dfd: Directory fd
+ * @tmpl: (type filename): template directory name
+ * @mode: permissions to create the temporary directory with
+ * @error: Error
+ *
+ * Similar to g_mkdtemp_full, but using openat.
+ */
+gboolean
+glnx_mkdtempat (int dfd,
+ gchar *tmpl,
+ int mode,
+ GError **error)
+{
+ char *XXXXXX;
+ int count;
+ static const char letters[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ static const int NLETTERS = sizeof (letters) - 1;
+ glong value;
+ GTimeVal tv;
+ static int counter = 0;
+
+ g_return_val_if_fail (tmpl != NULL, -1);
+
+ /* find the last occurrence of "XXXXXX" */
+ XXXXXX = g_strrstr (tmpl, "XXXXXX");
+
+ if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ "Invalid temporary directory template '%s'", tmpl);
+ return FALSE;
+ }
+
+ /* Get some more or less random data. */
+ g_get_current_time (&tv);
+ value = (tv.tv_usec ^ tv.tv_sec) + counter++;
+
+ for (count = 0; count < 100; value += 7777, ++count)
+ {
+ glong v = value;
+
+ /* Fill in the random bits. */
+ XXXXXX[0] = letters[v % NLETTERS];
+ v /= NLETTERS;
+ XXXXXX[1] = letters[v % NLETTERS];
+ v /= NLETTERS;
+ XXXXXX[2] = letters[v % NLETTERS];
+ v /= NLETTERS;
+ XXXXXX[3] = letters[v % NLETTERS];
+ v /= NLETTERS;
+ XXXXXX[4] = letters[v % NLETTERS];
+ v /= NLETTERS;
+ XXXXXX[5] = letters[v % NLETTERS];
+
+ if (mkdirat (dfd, tmpl, mode) == -1)
+ {
+ if (errno == EEXIST)
+ continue;
+
+ /* Any other error will apply also to other names we might
+ * try, and there are 2^32 or so of them, so give up now.
+ */
+ glnx_set_prefix_error_from_errno (error, "%s", "mkdirat");
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+ "mkstempat ran out of combinations to try.");
+ return FALSE;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-backport-autocleanups.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+G_BEGIN_DECLS
+
+/**
+ * glnx_dirfd_canonicalize:
+ * @fd: A directory file descriptor
+ *
+ * It's often convenient in programs to use `-1` for "unassigned fd",
+ * and also because gobject-introspection doesn't support `AT_FDCWD`,
+ * libglnx honors `-1` to mean `AT_FDCWD`. This small inline function
+ * canonicalizes `-1 -> AT_FDCWD`.
+ */
+static inline int
+glnx_dirfd_canonicalize (int fd)
+{
+ if (fd == -1)
+ return AT_FDCWD;
+ return fd;
+}
+
+struct GLnxDirFdIterator {
+ gboolean initialized;
+ int fd;
+ gpointer padding_data[4];
+};
+
+typedef struct GLnxDirFdIterator GLnxDirFdIterator;
+gboolean glnx_dirfd_iterator_init_at (int dfd, const char *path,
+ gboolean follow,
+ GLnxDirFdIterator *dfd_iter, GError **error);
+gboolean glnx_dirfd_iterator_init_take_fd (int dfd, GLnxDirFdIterator *dfd_iter, GError **error);
+gboolean glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter,
+ struct dirent **out_dent,
+ GCancellable *cancellable,
+ GError **error);
+void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter);
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxDirFdIterator, glnx_dirfd_iterator_clear)
+
+int glnx_opendirat_with_errno (int dfd,
+ const char *path,
+ gboolean follow);
+
+gboolean glnx_opendirat (int dfd,
+ const char *path,
+ gboolean follow,
+ int *out_fd,
+ GError **error);
+
+char *glnx_fdrel_abspath (int dfd,
+ const char *path);
+
+gboolean glnx_mkdtempat (int dfd,
+ gchar *tmpl,
+ int mode,
+ GError **error);
+
+G_END_DECLS
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <glnx-backport-autocleanups.h>
+#include <glnx-errors.h>
+
+void
+glnx_real_set_prefix_error_from_errno (GError **error,
+ gint errsv,
+ const char *format,
+ ...)
+{
+ if (!error)
+ return;
+ else
+ {
+ GString *buf = g_string_new ("");
+ va_list args;
+
+ va_start (args, format);
+ g_string_append_vprintf (buf, format, args);
+ va_end (args);
+
+ g_string_append (buf, ": ");
+ g_string_append (buf, g_strerror (errsv));
+
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (errsv),
+ buf->str);
+ g_string_free (buf, TRUE);
+ errno = errsv;
+ }
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-backport-autocleanups.h>
+#include <errno.h>
+
+G_BEGIN_DECLS
+
+#define glnx_set_error_from_errno(error) \
+ do { \
+ int errsv = errno; \
+ g_set_error_literal (error, G_IO_ERROR, \
+ g_io_error_from_errno (errsv), \
+ g_strerror (errsv)); \
+ errno = errsv; \
+ } while (0);
+
+#define glnx_set_prefix_error_from_errno(error, format, args...) \
+ do { \
+ int errsv = errno; \
+ glnx_real_set_prefix_error_from_errno (error, errsv, format, args); \
+ errno = errsv; \
+ } while (0);
+
+void glnx_real_set_prefix_error_from_errno (GError **error,
+ gint errsv,
+ const char *format,
+ ...) G_GNUC_PRINTF (3,4);
+
+G_END_DECLS
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * Portions derived from systemd:
+ * Copyright 2010 Lennart Poettering
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/ioctl.h>
+#include <sys/sendfile.h>
+#include <errno.h>
+/* See linux.git/fs/btrfs/ioctl.h */
+#define BTRFS_IOCTL_MAGIC 0x94
+#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
+
+#include <glnx-fdio.h>
+#include <glnx-dirfd.h>
+#include <glnx-errors.h>
+#include <glnx-xattrs.h>
+#include <glnx-backport-autoptr.h>
+#include <glnx-local-alloc.h>
+
+static guint8*
+glnx_fd_readall_malloc (int fd,
+ gsize *out_len,
+ gboolean nul_terminate,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success = FALSE;
+ const guint maxreadlen = 4096;
+ int res;
+ struct stat stbuf;
+ guint8* buf = NULL;
+ gsize buf_allocated;
+ gsize buf_size = 0;
+ gssize bytes_read;
+
+ do
+ res = fstat (fd, &stbuf);
+ while (G_UNLIKELY (res == -1 && errno == EINTR));
+ if (res == -1)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ if (S_ISREG (stbuf.st_mode) && stbuf.st_size > 0)
+ buf_allocated = stbuf.st_size;
+ else
+ buf_allocated = 16;
+
+ buf = g_malloc (buf_allocated);
+
+ while (TRUE)
+ {
+ gsize readlen = MIN (buf_allocated - buf_size, maxreadlen);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto out;
+
+ do
+ bytes_read = read (fd, buf + buf_size, readlen);
+ while (G_UNLIKELY (bytes_read == -1 && errno == EINTR));
+ if (G_UNLIKELY (bytes_read == -1))
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ if (bytes_read == 0)
+ break;
+
+ buf_size += bytes_read;
+ if (buf_allocated - buf_size < maxreadlen)
+ buf = g_realloc (buf, buf_allocated *= 2);
+ }
+
+ if (nul_terminate)
+ {
+ if (buf_allocated - buf_size == 0)
+ buf = g_realloc (buf, buf_allocated + 1);
+ buf[buf_size] = '\0';
+ }
+
+ success = TRUE;
+ out:
+ if (success)
+ {
+ *out_len = buf_size;
+ return buf;
+ }
+ g_free (buf);
+ return NULL;
+}
+
+/**
+ * glnx_fd_readall_bytes:
+ * @fd: A file descriptor
+ * @cancellable: Cancellable:
+ * @error: Error
+ *
+ * Read all data from file descriptor @fd into a #GBytes. It's
+ * recommended to only use this for small files.
+ *
+ * Returns: (transfer full): A newly allocated #GBytes
+ */
+GBytes *
+glnx_fd_readall_bytes (int fd,
+ GCancellable *cancellable,
+ GError **error)
+{
+ guint8 *buf;
+ gsize len;
+
+ buf = glnx_fd_readall_malloc (fd, &len, FALSE, cancellable, error);
+ if (!buf)
+ return NULL;
+
+ return g_bytes_new_take (buf, len);
+}
+
+/**
+ * glnx_fd_readall_utf8:
+ * @fd: A file descriptor
+ * @out_len: (out): Returned length
+ * @cancellable: Cancellable:
+ * @error: Error
+ *
+ * Read all data from file descriptor @fd, validating
+ * the result as UTF-8.
+ *
+ * Returns: (transfer full): A string validated as UTF-8, or %NULL on error.
+ */
+char *
+glnx_fd_readall_utf8 (int fd,
+ gsize *out_len,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success = FALSE;
+ guint8 *buf;
+ gsize len;
+
+ buf = glnx_fd_readall_malloc (fd, &len, TRUE, cancellable, error);
+ if (!buf)
+ goto out;
+
+ if (!g_utf8_validate ((char*)buf, len, NULL))
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "Invalid UTF-8");
+ goto out;
+ }
+
+ success = TRUE;
+ out:
+ if (success)
+ {
+ if (out_len)
+ *out_len = len;
+ return (char*)buf;
+ }
+ g_free (buf);
+ return NULL;
+}
+
+/**
+ * glnx_file_get_contents_utf8_at:
+ * @dfd: Directory file descriptor
+ * @subpath: Path relative to @dfd
+ * @out_len: (out) (allow-none): Optional length
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Read the entire contents of the file referred
+ * to by @dfd and @subpath, validate the result as UTF-8.
+ * The length is optionally stored in @out_len.
+ *
+ * Returns: (transfer full): UTF-8 validated text, or %NULL on error
+ */
+char *
+glnx_file_get_contents_utf8_at (int dfd,
+ const char *subpath,
+ gsize *out_len,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success = FALSE;
+ glnx_fd_close int fd = -1;
+ char *buf = NULL;
+ gsize len;
+
+ dfd = glnx_dirfd_canonicalize (dfd);
+
+ do
+ fd = openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC);
+ while (G_UNLIKELY (fd == -1 && errno == EINTR));
+ if (G_UNLIKELY (fd == -1))
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error);
+ if (G_UNLIKELY(!buf))
+ goto out;
+
+ success = TRUE;
+ out:
+ if (success)
+ {
+ if (out_len)
+ *out_len = len;
+ return buf;
+ }
+ g_free (buf);
+ return NULL;
+}
+
+/**
+ * glnx_readlinkat_malloc:
+ * @dfd: Directory file descriptor
+ * @subpath: Subpath
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Read the value of a symlink into a dynamically
+ * allocated buffer.
+ */
+char *
+glnx_readlinkat_malloc (int dfd,
+ const char *subpath,
+ GCancellable *cancellable,
+ GError **error)
+{
+ size_t l = 100;
+
+ dfd = glnx_dirfd_canonicalize (dfd);
+
+ for (;;)
+ {
+ char *c;
+ ssize_t n;
+
+ c = g_malloc (l);
+ n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1));
+ if (n < 0)
+ {
+ glnx_set_error_from_errno (error);
+ g_free (c);
+ return FALSE;
+ }
+
+ if ((size_t) n < l-1)
+ {
+ c[n] = 0;
+ return c;
+ }
+
+ g_free (c);
+ l *= 2;
+ }
+
+ g_assert_not_reached ();
+}
+
+static gboolean
+copy_symlink_at (int src_dfd,
+ const char *src_subpath,
+ const struct stat *src_stbuf,
+ int dest_dfd,
+ const char *dest_subpath,
+ GLnxFileCopyFlags copyflags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ g_autofree char *buf = NULL;
+
+ buf = glnx_readlinkat_malloc (src_dfd, src_subpath, cancellable, error);
+ if (!buf)
+ goto out;
+
+ if (TEMP_FAILURE_RETRY (symlinkat (buf, dest_dfd, dest_subpath)) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ if (!(copyflags & GLNX_FILE_COPY_NOXATTRS))
+ {
+ g_autoptr(GVariant) xattrs = NULL;
+
+ if (!glnx_dfd_name_get_all_xattrs (src_dfd, src_subpath, &xattrs,
+ cancellable, error))
+ goto out;
+
+ if (!glnx_dfd_name_set_all_xattrs (dest_dfd, dest_subpath, xattrs,
+ cancellable, error))
+ goto out;
+ }
+
+ if (TEMP_FAILURE_RETRY (fchownat (dest_dfd, dest_subpath,
+ src_stbuf->st_uid, src_stbuf->st_gid,
+ AT_SYMLINK_NOFOLLOW)) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+#define COPY_BUFFER_SIZE (16*1024)
+
+/* From systemd */
+
+static int btrfs_reflink(int infd, int outfd) {
+ int r;
+
+ g_return_val_if_fail(infd >= 0, -1);
+ g_return_val_if_fail(outfd >= 0, -1);
+
+ r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int loop_write(int fd, const void *buf, size_t nbytes) {
+ const uint8_t *p = buf;
+
+ g_return_val_if_fail(fd >= 0, -1);
+ g_return_val_if_fail(buf, -1);
+
+ errno = 0;
+
+ while (nbytes > 0) {
+ ssize_t k;
+
+ k = write(fd, p, nbytes);
+ if (k < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return -errno;
+ }
+
+ if (k == 0) /* Can't really happen */
+ return -EIO;
+
+ p += k;
+ nbytes -= k;
+ }
+
+ return 0;
+}
+
+static int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
+ bool try_sendfile = true;
+ int r;
+
+ g_return_val_if_fail (fdf >= 0, -1);
+ g_return_val_if_fail (fdt >= 0, -1);
+
+ /* Try btrfs reflinks first. */
+ if (try_reflink && max_bytes == (off_t) -1) {
+ r = btrfs_reflink(fdf, fdt);
+ if (r >= 0)
+ return r;
+ }
+
+ for (;;) {
+ size_t m = COPY_BUFFER_SIZE;
+ ssize_t n;
+
+ if (max_bytes != (off_t) -1) {
+
+ if (max_bytes <= 0)
+ return -EFBIG;
+
+ if ((off_t) m > max_bytes)
+ m = (size_t) max_bytes;
+ }
+
+ /* First try sendfile(), unless we already tried */
+ if (try_sendfile) {
+
+ n = sendfile(fdt, fdf, NULL, m);
+ if (n < 0) {
+ if (errno != EINVAL && errno != ENOSYS)
+ return -errno;
+
+ try_sendfile = false;
+ /* use fallback below */
+ } else if (n == 0) /* EOF */
+ break;
+ else if (n > 0)
+ /* Succcess! */
+ goto next;
+ }
+
+ /* As a fallback just copy bits by hand */
+ {
+ char buf[m];
+
+ n = read(fdf, buf, m);
+ if (n < 0)
+ return -errno;
+ if (n == 0) /* EOF */
+ break;
+
+ r = loop_write(fdt, buf, (size_t) n);
+ if (r < 0)
+ return r;
+ }
+
+ next:
+ if (max_bytes != (off_t) -1) {
+ g_assert(max_bytes >= n);
+ max_bytes -= n;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * glnx_file_copy_at:
+ * @src_dfd: Source directory fd
+ * @src_subpath: Subpath relative to @src_dfd
+ * @dest_dfd: Target directory fd
+ * @dest_subpath: Destination name
+ * @copyflags: Flags
+ * @cancellable: cancellable
+ * @error: Error
+ *
+ * Perform a full copy of the regular file or
+ * symbolic link from @src_subpath to @dest_subpath.
+ *
+ * If @src_subpath is anything other than a regular
+ * file or symbolic link, an error will be returned.
+ */
+gboolean
+glnx_file_copy_at (int src_dfd,
+ const char *src_subpath,
+ struct stat *src_stbuf,
+ int dest_dfd,
+ const char *dest_subpath,
+ GLnxFileCopyFlags copyflags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ int r;
+ int dest_open_flags;
+ struct timespec ts[2];
+ glnx_fd_close int src_fd = -1;
+ glnx_fd_close int dest_fd = -1;
+ struct stat local_stbuf;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto out;
+
+ src_dfd = glnx_dirfd_canonicalize (src_dfd);
+ dest_dfd = glnx_dirfd_canonicalize (dest_dfd);
+
+ /* Automatically do stat() if no stat buffer was supplied */
+ if (!src_stbuf)
+ {
+ if (fstatat (src_dfd, src_subpath, &local_stbuf, AT_SYMLINK_NOFOLLOW) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ src_stbuf = &local_stbuf;
+ }
+
+ if (S_ISLNK (src_stbuf->st_mode))
+ {
+ return copy_symlink_at (src_dfd, src_subpath, src_stbuf,
+ dest_dfd, dest_subpath,
+ copyflags,
+ cancellable, error);
+ }
+ else if (!S_ISREG (src_stbuf->st_mode))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Cannot copy non-regular/non-symlink file: %s", src_subpath);
+ goto out;
+ }
+
+ src_fd = TEMP_FAILURE_RETRY (openat (src_dfd, src_subpath, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW));
+ if (src_fd == -1)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ dest_open_flags = O_WRONLY | O_CREAT | O_CLOEXEC | O_NOCTTY;
+ if (!(copyflags & GLNX_FILE_COPY_OVERWRITE))
+ dest_open_flags |= O_EXCL;
+ else
+ dest_open_flags |= O_TRUNC;
+
+ dest_fd = TEMP_FAILURE_RETRY (openat (dest_dfd, dest_subpath, dest_open_flags, src_stbuf->st_mode));
+ if (dest_fd == -1)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ r = copy_bytes (src_fd, dest_fd, (off_t) -1, TRUE);
+ if (r < 0)
+ {
+ errno = -r;
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ if (fchown (dest_fd, src_stbuf->st_uid, src_stbuf->st_gid) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ if (fchmod (dest_fd, src_stbuf->st_mode & 07777) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ ts[0] = src_stbuf->st_atim;
+ ts[1] = src_stbuf->st_mtim;
+ (void) futimens (dest_fd, ts);
+
+ if (!(copyflags & GLNX_FILE_COPY_NOXATTRS))
+ {
+ g_autoptr(GVariant) xattrs = NULL;
+
+ if (!glnx_fd_get_all_xattrs (src_fd, &xattrs,
+ cancellable, error))
+ goto out;
+
+ if (!glnx_fd_set_all_xattrs (dest_fd, xattrs,
+ cancellable, error))
+ goto out;
+ }
+
+ if (copyflags & GLNX_FILE_COPY_DATASYNC)
+ {
+ if (fdatasync (dest_fd) < 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+
+ r = close (dest_fd);
+ dest_fd = -1;
+ if (r < 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ (void) unlinkat (dest_dfd, dest_subpath, 0);
+ return ret;
+}
+
+/**
+ * glnx_file_replace_contents_at:
+ * @dfd: Directory fd
+ * @subpath: Subpath
+ * @buf: (array len=len) (element-type guint8): File contents
+ * @len: Length (if `-1`, assume @buf is `NUL` terminated)
+ * @flags: Flags
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Create a new file, atomically replacing the contents of @subpath
+ * (relative to @dfd) with @buf. By default, if the file already
+ * existed, fdatasync() will be used before rename() to ensure stable
+ * contents. This and other behavior can be controlled via @flags.
+ *
+ * Note that no metadata from the existing file is preserved, such as
+ * uid/gid or extended attributes. The default mode will be `0666`,
+ * modified by umask.
+ */
+gboolean
+glnx_file_replace_contents_at (int dfd,
+ const char *subpath,
+ const guint8 *buf,
+ gsize len,
+ GLnxFileReplaceFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return glnx_file_replace_contents_with_perms_at (dfd, subpath, buf, len,
+ (mode_t) -1, (uid_t) -1, (gid_t) -1,
+ flags, cancellable, error);
+
+}
+
+/**
+ * glnx_file_replace_contents_with_perms_at:
+ * @dfd: Directory fd
+ * @subpath: Subpath
+ * @buf: (array len=len) (element-type guint8): File contents
+ * @len: Length (if `-1`, assume @buf is `NUL` terminated)
+ * @mode: File mode; if `-1`, use `0666 - umask`
+ * @flags: Flags
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Like glnx_file_replace_contents_at(), but also supports
+ * setting mode, and uid/gid.
+ */
+gboolean
+glnx_file_replace_contents_with_perms_at (int dfd,
+ const char *subpath,
+ const guint8 *buf,
+ gsize len,
+ mode_t mode,
+ uid_t uid,
+ gid_t gid,
+ GLnxFileReplaceFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ int r;
+ /* We use the /proc/self trick as there's no mkostemp_at() yet */
+ g_autofree char *tmppath = g_strdup_printf ("/proc/self/fd/%d/.tmpXXXXXX", dfd);
+ glnx_fd_close int fd = -1;
+
+ dfd = glnx_dirfd_canonicalize (dfd);
+
+ if ((fd = g_mkstemp_full (tmppath, O_WRONLY | O_CLOEXEC,
+ mode == (mode_t) -1 ? 0666 : mode)) == -1)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ if (len == -1)
+ len = strlen ((char*)buf);
+
+ /* Note that posix_fallocate does *not* set errno but returns it. */
+ r = posix_fallocate (fd, 0, len);
+ if (r != 0)
+ {
+ errno = r;
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ if ((r = loop_write (fd, buf, len)) != 0)
+ {
+ errno = -r;
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ if (!(flags & GLNX_FILE_REPLACE_NODATASYNC))
+ {
+ struct stat stbuf;
+ gboolean do_sync;
+
+ if (fstatat (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
+ {
+ if (errno != ENOENT)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0;
+ }
+ else
+ do_sync = TRUE;
+
+ if (do_sync)
+ {
+ if (fdatasync (fd) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+ }
+
+ if (uid != (uid_t) -1)
+ {
+ if (fchown (fd, uid, gid) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+
+ /* If a mode was forced, override umask */
+ if (mode != (mode_t) -1)
+ {
+ if (fchmod (fd, mode) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+
+ if (renameat (dfd, tmppath, dfd, subpath) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-backport-autocleanups.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/xattr.h>
+/* From systemd/src/shared/util.h */
+/* When we include libgen.h because we need dirname() we immediately
+ * undefine basename() since libgen.h defines it as a macro to the XDG
+ * version which is really broken. */
+#include <libgen.h>
+#undef basename
+
+G_BEGIN_DECLS
+
+/* Irritatingly, g_basename() which is what we want
+ * is deprecated.
+ */
+static inline
+const char *glnx_basename (const char *path)
+{
+ return (basename) (path);
+}
+
+GBytes *
+glnx_fd_readall_bytes (int fd,
+ GCancellable *cancellable,
+ GError **error);
+
+char *
+glnx_fd_readall_utf8 (int fd,
+ gsize *out_len,
+ GCancellable *cancellable,
+ GError **error);
+
+char *
+glnx_file_get_contents_utf8_at (int dfd,
+ const char *subpath,
+ gsize *out_len,
+ GCancellable *cancellable,
+ GError **error);
+
+/**
+ * GLnxFileReplaceFlags:
+ * @GLNX_FILE_REPLACE_DATASYNC_NEW: Call fdatasync() even if the file did not exist
+ * @GLNX_FILE_REPLACE_NODATASYNC: Never call fdatasync()
+ *
+ * Flags controlling file replacement.
+ */
+typedef enum {
+ GLNX_FILE_REPLACE_DATASYNC_NEW = (1 << 0),
+ GLNX_FILE_REPLACE_NODATASYNC = (1 << 1),
+} GLnxFileReplaceFlags;
+
+gboolean
+glnx_file_replace_contents_at (int dfd,
+ const char *subpath,
+ const guint8 *buf,
+ gsize len,
+ GLnxFileReplaceFlags flags,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean
+glnx_file_replace_contents_with_perms_at (int dfd,
+ const char *subpath,
+ const guint8 *buf,
+ gsize len,
+ mode_t mode,
+ uid_t uid,
+ gid_t gid,
+ GLnxFileReplaceFlags flags,
+ GCancellable *cancellable,
+ GError **error);
+
+char *
+glnx_readlinkat_malloc (int dfd,
+ const char *subpath,
+ GCancellable *cancellable,
+ GError **error);
+
+typedef enum {
+ GLNX_FILE_COPY_OVERWRITE,
+ GLNX_FILE_COPY_NOXATTRS,
+ GLNX_FILE_COPY_DATASYNC
+} GLnxFileCopyFlags;
+
+gboolean
+glnx_file_copy_at (int src_dfd,
+ const char *src_subpath,
+ struct stat *src_stbuf,
+ int dest_dfd,
+ const char *dest_subpath,
+ GLnxFileCopyFlags copyflags,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Portions derived from src/nspawn/nspawn.c:
+ * Copyright 2010 Lennart Poettering
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib-unix.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <gio/gunixoutputstream.h>
+
+#include "glnx-libcontainer.h"
+
+#include "glnx-backport-autocleanups.h"
+#include "glnx-local-alloc.h"
+
+static void _perror_fatal (const char *message) __attribute__ ((noreturn));
+
+static void
+_perror_fatal (const char *message)
+{
+ perror (message);
+ exit (1);
+}
+
+typedef enum {
+ CONTAINER_UNINIT = 0,
+ CONTAINER_YES = 1,
+ CONTAINER_NO = 2
+} ContainerDetectionState;
+
+static gboolean
+currently_in_container (void)
+{
+ static gsize container_detected = CONTAINER_UNINIT;
+
+ if (g_once_init_enter (&container_detected))
+ {
+ ContainerDetectionState tmp_state = CONTAINER_NO;
+ struct stat stbuf;
+
+ /* http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface/ */
+ if (getenv ("container") != NULL
+ || stat ("/.dockerinit", &stbuf) == 0)
+ tmp_state = CONTAINER_YES;
+ /* But since Docker isn't on board, yet, so...
+ http://stackoverflow.com/questions/23513045/how-to-check-if-a-process-is-running-inside-docker-container */
+ g_once_init_leave (&container_detected, tmp_state);
+ }
+ return container_detected == CONTAINER_YES;
+}
+
+#if 0
+static gboolean
+glnx_libcontainer_bind_mount_readonly (const char *path, GError **error)
+{
+ gboolean ret = FALSE;
+
+ if (mount (path, path, NULL, MS_BIND | MS_PRIVATE, NULL) != 0)
+ {
+ int errsv = errno;
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "mount(%s, MS_BIND): %s",
+ path,
+ g_strerror (errsv));
+ goto out;
+ }
+ if (mount (path, path, NULL, MS_BIND | MS_PRIVATE | MS_REMOUNT | MS_RDONLY, NULL) != 0)
+ {
+ int errsv = errno;
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "mount(%s, MS_BIND | MS_RDONLY): %s",
+ path,
+ g_strerror (errsv));
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+#endif
+
+/* Based on code from nspawn.c */
+static int
+glnx_libcontainer_make_api_mounts (const char *dest)
+{
+ typedef struct MountPoint {
+ const char *what;
+ const char *where;
+ const char *type;
+ const char *options;
+ unsigned long flags;
+ gboolean fatal;
+ } MountPoint;
+
+ static const MountPoint mount_table[] = {
+ { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE },
+ { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, TRUE }, /* Bind mount first */
+ { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, TRUE }, /* Then, make it r/o */
+ { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE },
+ { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, TRUE },
+ { "devpts", "/dev/pts", "devpts","newinstance,ptmxmode=0666,mode=620,gid=5", MS_NOSUID|MS_NOEXEC, TRUE },
+ { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, TRUE },
+ { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, TRUE },
+ { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, FALSE }, /* Bind mount first */
+ { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, FALSE }, /* Then, make it r/o */
+ };
+
+ unsigned k;
+
+ for (k = 0; k < G_N_ELEMENTS(mount_table); k++)
+ {
+ g_autofree char *where = NULL;
+ int t;
+
+ where = g_build_filename (dest, mount_table[k].where, NULL);
+
+ t = mkdir (where, 0755);
+ if (t < 0 && errno != EEXIST)
+ {
+ if (!mount_table[k].fatal)
+ continue;
+ return -1;
+ }
+
+ if (mount (mount_table[k].what,
+ where,
+ mount_table[k].type,
+ mount_table[k].flags,
+ mount_table[k].options) < 0)
+ {
+ if (errno == ENOENT && !mount_table[k].fatal)
+ continue;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+glnx_libcontainer_prep_dev (const char *dest_devdir)
+{
+ glnx_fd_close int src_fd = -1;
+ glnx_fd_close int dest_fd = -1;
+ struct stat stbuf;
+ guint i;
+ static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" };
+
+ src_fd = openat (AT_FDCWD, "/dev", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
+ if (src_fd == -1)
+ return -1;
+
+ dest_fd = openat (AT_FDCWD, dest_devdir, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
+ if (dest_fd == -1)
+ return -1;
+
+ for (i = 0; i < G_N_ELEMENTS (devnodes); i++)
+ {
+ const char *nodename = devnodes[i];
+
+ if (fstatat (src_fd, nodename, &stbuf, 0) == -1)
+ {
+ if (errno == ENOENT)
+ continue;
+ else
+ return -1;
+ }
+
+ if (mknodat (dest_fd, nodename, stbuf.st_mode, stbuf.st_rdev) != 0)
+ return -1;
+ if (fchmodat (dest_fd, nodename, stbuf.st_mode, 0) != 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+pid_t
+glnx_libcontainer_run_chroot_private (const char *dest,
+ const char *binary,
+ char **argv)
+{
+ /* Make most new namespaces; note our use of CLONE_NEWNET means we
+ * have no networking in the container root.
+ */
+ const int cloneflags =
+ SIGCHLD | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_SYSVSEM | CLONE_NEWUTS;
+ pid_t child;
+ gboolean in_container = currently_in_container ();
+
+ if (!in_container)
+ {
+ if ((child = syscall (__NR_clone, cloneflags, NULL)) < 0)
+ return -1;
+ }
+ else
+ {
+ if ((child = fork ()) < 0)
+ return -1;
+ }
+
+ if (child != 0)
+ return child;
+
+ if (!in_container)
+ {
+ if (mount (NULL, "/", "none", MS_PRIVATE | MS_REC, NULL) != 0)
+ {
+ if (errno == EINVAL)
+ {
+ /* Ok, we may be inside a mock chroot or the like. In
+ * that case, let's just fall back to not
+ * containerizing.
+ */
+ in_container = TRUE;
+ }
+ else
+ _perror_fatal ("mount: ");
+ }
+
+ if (!in_container)
+ {
+ if (mount (NULL, "/", "none", MS_PRIVATE | MS_REMOUNT | MS_NOSUID, NULL) != 0)
+ _perror_fatal ("mount (MS_NOSUID): ");
+ }
+ }
+
+ if (chdir (dest) != 0)
+ _perror_fatal ("chdir: ");
+
+ if (!in_container)
+ {
+ if (glnx_libcontainer_make_api_mounts (dest) != 0)
+ _perror_fatal ("preparing api mounts: ");
+
+ if (glnx_libcontainer_prep_dev ("dev") != 0)
+ _perror_fatal ("preparing /dev: ");
+
+ if (mount (".", ".", NULL, MS_BIND | MS_PRIVATE, NULL) != 0)
+ _perror_fatal ("mount (MS_BIND)");
+
+ if (mount (dest, "/", NULL, MS_MOVE, NULL) != 0)
+ _perror_fatal ("mount (MS_MOVE)");
+ }
+
+ if (chroot (".") != 0)
+ _perror_fatal ("chroot: ");
+
+ if (chdir ("/") != 0)
+ _perror_fatal ("chdir: ");
+
+ if (binary[0] == '/')
+ {
+ if (execv (binary, argv) != 0)
+ _perror_fatal ("execv: ");
+ }
+ else
+ {
+ /* Set PATH to something sane. */
+ setenv ("PATH", "/usr/sbin:/usr/bin", 1);
+
+ if (execvp (binary, argv) != 0)
+ _perror_fatal ("execvp: ");
+ }
+
+ g_assert_not_reached ();
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/prctl.h>
+#include <sys/fsuid.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <sys/capability.h>
+#include <sched.h>
+
+pid_t glnx_libcontainer_run_chroot_private (const char *dest,
+ const char *binary,
+ char **argv);
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012,2015 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "glnx-local-alloc.h"
+
+/**
+ * SECTION:glnxlocalalloc
+ * @title: GLnx local allocation
+ * @short_description: Release local variables automatically when they go out of scope
+ *
+ * These macros leverage the GCC extension __attribute__ ((cleanup))
+ * to allow calling a cleanup function such as g_free() when a
+ * variable goes out of scope. See <ulink
+ * url="http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html">
+ * for more information on the attribute.
+ *
+ * The provided macros make it easy to use the cleanup attribute for
+ * types that come with GLib. The primary two are #glnx_free and
+ * #glnx_unref_object, which correspond to g_free() and
+ * g_object_unref(), respectively.
+ *
+ * The rationale behind this is that particularly when handling error
+ * paths, it can be very tricky to ensure the right variables are
+ * freed. With this, one simply applies glnx_unref_object to a
+ * locally-allocated #GFile for example, and it will be automatically
+ * unreferenced when it goes out of scope.
+ *
+ * Note - you should only use these macros for <emphasis>stack
+ * allocated</emphasis> variables. They don't provide garbage
+ * collection or let you avoid freeing things. They're simply a
+ * compiler assisted deterministic mechanism for calling a cleanup
+ * function when a stack frame ends.
+ *
+ * <example id="gs-lfree"><title>Calling g_free automatically</title>
+ * <programlisting>
+ *
+ * GFile *
+ * create_file (GError **error)
+ * {
+ * glnx_free char *random_id = NULL;
+ *
+ * if (!prepare_file (error))
+ * return NULL;
+ *
+ * random_id = alloc_random_id ();
+ *
+ * return create_file_real (error);
+ * // Note that random_id is freed here automatically
+ * }
+ * </programlisting>
+ * </example>
+ *
+ */
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GLNX_DEFINE_CLEANUP_FUNCTION(Type, name, func) \
+ static inline void name (void *v) \
+ { \
+ func (*(Type*)v); \
+ }
+
+#define GLNX_DEFINE_CLEANUP_FUNCTION0(Type, name, func) \
+ static inline void name (void *v) \
+ { \
+ if (*(Type*)v) \
+ func (*(Type*)v); \
+ }
+
+/**
+ * glnx_free:
+ *
+ * Call g_free() on a variable location when it goes out of scope.
+ */
+#define glnx_free __attribute__ ((cleanup(glnx_local_free)))
+#ifdef GLNX_GSYSTEM_COMPAT
+#define gs_free __attribute__ ((cleanup(glnx_local_free)))
+#endif
+GLNX_DEFINE_CLEANUP_FUNCTION(void*, glnx_local_free, g_free)
+
+/**
+ * glnx_unref_object:
+ *
+ * Call g_object_unref() on a variable location when it goes out of
+ * scope. Note that unlike g_object_unref(), the variable may be
+ * %NULL.
+ */
+#define glnx_unref_object __attribute__ ((cleanup(glnx_local_obj_unref)))
+#ifdef GLNX_GSYSTEM_COMPAT
+#define gs_unref_object __attribute__ ((cleanup(glnx_local_obj_unref)))
+#endif
+GLNX_DEFINE_CLEANUP_FUNCTION0(GObject*, glnx_local_obj_unref, g_object_unref)
+
+/**
+ * glnx_unref_variant:
+ *
+ * Call g_variant_unref() on a variable location when it goes out of
+ * scope. Note that unlike g_variant_unref(), the variable may be
+ * %NULL.
+ */
+#define glnx_unref_variant __attribute__ ((cleanup(glnx_local_variant_unref)))
+#ifdef GLNX_GSYSTEM_COMPAT
+#define gs_unref_variant __attribute__ ((cleanup(glnx_local_variant_unref)))
+#endif
+GLNX_DEFINE_CLEANUP_FUNCTION0(GVariant*, glnx_local_variant_unref, g_variant_unref)
+
+/**
+ * glnx_free_variant_iter:
+ *
+ * Call g_variant_iter_free() on a variable location when it goes out of
+ * scope.
+ */
+#define glnx_free_variant_iter __attribute__ ((cleanup(glnx_local_variant_iter_free)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GVariantIter*, glnx_local_variant_iter_free, g_variant_iter_free)
+
+/**
+ * glnx_free_variant_builder:
+ *
+ * Call g_variant_builder_unref() on a variable location when it goes out of
+ * scope.
+ */
+#define glnx_unref_variant_builder __attribute__ ((cleanup(glnx_local_variant_builder_unref)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GVariantBuilder*, glnx_local_variant_builder_unref, g_variant_builder_unref)
+
+/**
+ * glnx_unref_array:
+ *
+ * Call g_array_unref() on a variable location when it goes out of
+ * scope. Note that unlike g_array_unref(), the variable may be
+ * %NULL.
+
+ */
+#define glnx_unref_array __attribute__ ((cleanup(glnx_local_array_unref)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GArray*, glnx_local_array_unref, g_array_unref)
+
+/**
+ * glnx_unref_ptrarray:
+ *
+ * Call g_ptr_array_unref() on a variable location when it goes out of
+ * scope. Note that unlike g_ptr_array_unref(), the variable may be
+ * %NULL.
+
+ */
+#define glnx_unref_ptrarray __attribute__ ((cleanup(glnx_local_ptrarray_unref)))
+#ifdef GLNX_GSYSTEM_COMPAT
+#define gs_unref_ptrarray __attribute__ ((cleanup(glnx_local_ptrarray_unref)))
+#endif
+GLNX_DEFINE_CLEANUP_FUNCTION0(GPtrArray*, glnx_local_ptrarray_unref, g_ptr_array_unref)
+
+/**
+ * glnx_unref_hashtable:
+ *
+ * Call g_hash_table_unref() on a variable location when it goes out
+ * of scope. Note that unlike g_hash_table_unref(), the variable may
+ * be %NULL.
+ */
+#define glnx_unref_hashtable __attribute__ ((cleanup(glnx_local_hashtable_unref)))
+#ifdef GLNX_GSYSTEM_COMPAT
+#define gs_unref_hashtable __attribute__ ((cleanup(glnx_local_hashtable_unref)))
+#endif
+GLNX_DEFINE_CLEANUP_FUNCTION0(GHashTable*, glnx_local_hashtable_unref, g_hash_table_unref)
+
+/**
+ * glnx_free_list:
+ *
+ * Call g_list_free() on a variable location when it goes out
+ * of scope.
+ */
+#define glnx_free_list __attribute__ ((cleanup(glnx_local_free_list)))
+GLNX_DEFINE_CLEANUP_FUNCTION(GList*, glnx_local_free_list, g_list_free)
+
+/**
+ * glnx_free_slist:
+ *
+ * Call g_slist_free() on a variable location when it goes out
+ * of scope.
+ */
+#define glnx_free_slist __attribute__ ((cleanup(glnx_local_free_slist)))
+GLNX_DEFINE_CLEANUP_FUNCTION(GSList*, glnx_local_free_slist, g_slist_free)
+
+/**
+ * glnx_free_checksum:
+ *
+ * Call g_checksum_free() on a variable location when it goes out
+ * of scope. Note that unlike g_checksum_free(), the variable may
+ * be %NULL.
+ */
+#define glnx_free_checksum __attribute__ ((cleanup(glnx_local_checksum_free)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GChecksum*, glnx_local_checksum_free, g_checksum_free)
+
+/**
+ * glnx_unref_bytes:
+ *
+ * Call g_bytes_unref() on a variable location when it goes out
+ * of scope. Note that unlike g_bytes_unref(), the variable may
+ * be %NULL.
+ */
+#define glnx_unref_bytes __attribute__ ((cleanup(glnx_local_bytes_unref)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GBytes*, glnx_local_bytes_unref, g_bytes_unref)
+
+/**
+ * glnx_strfreev:
+ *
+ * Call g_strfreev() on a variable location when it goes out of scope.
+ */
+#define glnx_strfreev __attribute__ ((cleanup(glnx_local_strfreev)))
+GLNX_DEFINE_CLEANUP_FUNCTION(char**, glnx_local_strfreev, g_strfreev)
+
+/**
+ * glnx_free_error:
+ *
+ * Call g_error_free() on a variable location when it goes out of scope.
+ */
+#define glnx_free_error __attribute__ ((cleanup(glnx_local_free_error)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GError*, glnx_local_free_error, g_error_free)
+
+/**
+ * glnx_unref_keyfile:
+ *
+ * Call g_key_file_unref() on a variable location when it goes out of scope.
+ */
+#define glnx_unref_keyfile __attribute__ ((cleanup(glnx_local_keyfile_unref)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, glnx_local_keyfile_unref, g_key_file_unref)
+
+static inline void
+glnx_cleanup_close_fdp (int *fdp)
+{
+ int fd;
+
+ g_assert (fdp);
+
+ fd = *fdp;
+ if (fd != -1)
+ (void) close (fd);
+}
+
+/**
+ * glnx_fd_close:
+ *
+ * Call close() on a variable location when it goes out of scope.
+ */
+#define glnx_fd_close __attribute__((cleanup(glnx_cleanup_close_fdp)))
+
+static inline int
+glnx_steal_fd (int *fdp)
+{
+ int fd = *fdp;
+ *fdp = -1;
+ return fd;
+}
+
+G_END_DECLS
--- /dev/null
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+ Now copied into libglnx:
+ - Use GError
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2015 Colin Walters <walters@verbum.org>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "glnx-lockfile.h"
+#include "glnx-errors.h"
+#include "glnx-fdio.h"
+#include "glnx-backport-autocleanups.h"
+#include "glnx-local-alloc.h"
+
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+
+/**
+ * glnx_make_lock_file:
+ * @dfd: Directory file descriptor (if not `AT_FDCWD`, must have lifetime `>=` @out_lock)
+ * @p: Path
+ * @operation: one of `LOCK_SH`, `LOCK_EX`, `LOCK_UN`, as passed to flock()
+ * @out_lock: (out) (caller allocates): Return location for lock
+ * @error: Error
+ *
+ * Block until a lock file named @p (relative to @dfd) can be created,
+ * using the flags in @operation, returning the lock data in the
+ * caller-allocated location @out_lock.
+ *
+ * This API wraps new-style process locking if available, otherwise
+ * falls back to BSD locks.
+ */
+gboolean
+glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_lock, GError **error) {
+ gboolean ret = FALSE;
+ glnx_fd_close int fd = -1;
+ g_autofree char *t = NULL;
+ int r;
+
+ /*
+ * We use UNPOSIX locks if they are available. They have nice
+ * semantics, and are mostly compatible with NFS. However,
+ * they are only available on new kernels. When we detect we
+ * are running on an older kernel, then we fall back to good
+ * old BSD locks. They also have nice semantics, but are
+ * slightly problematic on NFS, where they are upgraded to
+ * POSIX locks, even though locally they are orthogonal to
+ * POSIX locks.
+ */
+
+ t = g_strdup(p);
+
+ for (;;) {
+#ifdef F_OFD_SETLK
+ struct flock fl = {
+ .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
+ .l_whence = SEEK_SET,
+ };
+#endif
+ struct stat st;
+
+ fd = openat(dfd, p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
+ if (fd < 0) {
+ glnx_set_error_from_errno(error);
+ goto out;
+ }
+
+ /* Unfortunately, new locks are not in RHEL 7.1 glibc */
+#ifdef F_OFD_SETLK
+ r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl);
+#else
+ r = -1;
+ errno = EINVAL;
+#endif
+ if (r < 0) {
+
+ /* If the kernel is too old, use good old BSD locks */
+ if (errno == EINVAL)
+ r = flock(fd, operation);
+
+ if (r < 0) {
+ glnx_set_error_from_errno(error);
+ goto out;
+ }
+ }
+
+ /* If we acquired the lock, let's check if the file
+ * still exists in the file system. If not, then the
+ * previous exclusive owner removed it and then closed
+ * it. In such a case our acquired lock is worthless,
+ * hence try again. */
+
+ r = fstat(fd, &st);
+ if (r < 0) {
+ glnx_set_error_from_errno(error);
+ goto out;
+ }
+ if (st.st_nlink > 0)
+ break;
+
+ (void) close(fd);
+ fd = -1;
+ }
+
+ /* Note that if this is not AT_FDCWD, the caller takes responsibility
+ * for the fd's lifetime being >= that of the lock.
+ */
+ out_lock->dfd = dfd;
+ out_lock->path = t;
+ out_lock->fd = fd;
+ out_lock->operation = operation;
+
+ fd = -1;
+ t = NULL;
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+void glnx_release_lock_file(GLnxLockFile *f) {
+ int r;
+
+ if (!f)
+ return;
+
+ if (f->path) {
+
+ /* If we are the exclusive owner we can safely delete
+ * the lock file itself. If we are not the exclusive
+ * owner, we can try becoming it. */
+
+ if (f->fd >= 0 &&
+ (f->operation & ~LOCK_NB) == LOCK_SH) {
+#ifdef F_OFD_SETLK
+ static const struct flock fl = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ };
+
+ r = fcntl(f->fd, F_OFD_SETLK, &fl);
+#else
+ r = -1;
+ errno = EINVAL;
+#endif
+ if (r < 0 && errno == EINVAL)
+ r = flock(f->fd, LOCK_EX|LOCK_NB);
+
+ if (r >= 0)
+ f->operation = LOCK_EX|LOCK_NB;
+ }
+
+ if ((f->operation & ~LOCK_NB) == LOCK_EX) {
+ (void) unlinkat(f->dfd, f->path, 0);
+ }
+
+ g_free(f->path);
+ f->path = NULL;
+ }
+
+ (void) close (f->fd);
+ f->fd = -1;
+ f->operation = 0;
+}
--- /dev/null
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+ Copyright 2015 Colin Walters <walters@verbum.org>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "glnx-backport-autoptr.h"
+
+typedef struct GLnxLockFile {
+ int dfd;
+ char *path;
+ int fd;
+ int operation;
+} GLnxLockFile;
+
+gboolean glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *ret, GError **error);
+void glnx_release_lock_file(GLnxLockFile *f);
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxLockFile, glnx_release_lock_file)
+
+#define GLNX_LOCK_FILE_INIT { .fd = -1, .dfd = AT_FDCWD, .path = NULL }
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glnx-shutil.h>
+#include <glnx-errors.h>
+#include <glnx-local-alloc.h>
+
+static unsigned char
+struct_stat_to_dt (struct stat *stbuf)
+{
+ if (S_ISDIR (stbuf->st_mode))
+ return DT_DIR;
+ if (S_ISREG (stbuf->st_mode))
+ return DT_REG;
+ if (S_ISCHR (stbuf->st_mode))
+ return DT_CHR;
+ if (S_ISBLK (stbuf->st_mode))
+ return DT_BLK;
+ if (S_ISFIFO (stbuf->st_mode))
+ return DT_FIFO;
+ if (S_ISLNK (stbuf->st_mode))
+ return DT_LNK;
+ if (S_ISSOCK (stbuf->st_mode))
+ return DT_SOCK;
+ return DT_UNKNOWN;
+}
+
+static gboolean
+glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ struct dirent *dent;
+
+ while (TRUE)
+ {
+ if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, cancellable, error))
+ goto out;
+
+ if (dent == NULL)
+ break;
+
+ if (dent->d_type == DT_UNKNOWN)
+ {
+ struct stat stbuf;
+ if (fstatat (dfd_iter->fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
+ {
+ if (errno == ENOENT)
+ continue;
+ else
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+ dent->d_type = struct_stat_to_dt (&stbuf);
+ /* Assume unknown types are just treated like regular files */
+ if (dent->d_type == DT_UNKNOWN)
+ dent->d_type = DT_REG;
+ }
+
+ if (dent->d_type == DT_DIR)
+ {
+ g_auto(GLnxDirFdIterator) child_dfd_iter = { 0, };
+
+ if (!glnx_dirfd_iterator_init_at (dfd_iter->fd, dent->d_name, FALSE,
+ &child_dfd_iter, error))
+ goto out;
+
+ if (!glnx_shutil_rm_rf_children (&child_dfd_iter, cancellable, error))
+ goto out;
+
+ if (unlinkat (dfd_iter->fd, dent->d_name, AT_REMOVEDIR) == -1)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+ else
+ {
+ if (unlinkat (dfd_iter->fd, dent->d_name, 0) == -1)
+ {
+ if (errno != ENOENT)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+ }
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+/**
+ * glnx_shutil_rm_rf_at:
+ * @dfd: A directory file descriptor, or `AT_FDCWD` or `-1` for current
+ * @path: Path
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Recursively delete the filename referenced by the combination of
+ * the directory fd @dfd and @path; it may be a file or directory. No
+ * error is thrown if @path does not exist.
+ */
+gboolean
+glnx_shutil_rm_rf_at (int dfd,
+ const char *path,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ glnx_fd_close int target_dfd = -1;
+ g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
+
+ dfd = glnx_dirfd_canonicalize (dfd);
+
+ /* With O_NOFOLLOW first */
+ target_dfd = openat (dfd, path,
+ O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
+
+ if (target_dfd == -1)
+ {
+ int errsv = errno;
+ if (errsv == ENOENT)
+ {
+ ;
+ }
+ else if (errsv == ENOTDIR || errsv == ELOOP)
+ {
+ if (unlinkat (dfd, path, 0) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+ else
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+ else
+ {
+ if (!glnx_dirfd_iterator_init_take_fd (target_dfd, &dfd_iter, error))
+ goto out;
+ target_dfd = -1;
+
+ if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error))
+ goto out;
+
+ if (unlinkat (dfd, path, AT_REMOVEDIR) == -1)
+ {
+ int errsv = errno;
+ if (errsv != ENOENT)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
+mkdir_p_at_internal (int dfd,
+ char *path,
+ int mode,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gboolean did_recurse = FALSE;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto out;
+
+ again:
+ if (mkdirat (dfd, path, mode) == -1)
+ {
+ if (errno == ENOENT)
+ {
+ char *lastslash;
+
+ g_assert (!did_recurse);
+
+ lastslash = strrchr (path, '/');
+ g_assert (lastslash != NULL);
+ /* Note we can mutate the buffer as we dup'd it */
+ *lastslash = '\0';
+
+ if (!glnx_shutil_mkdir_p_at (dfd, path, mode,
+ cancellable, error))
+ goto out;
+
+ /* Now restore it for another mkdir attempt */
+ *lastslash = '/';
+
+ did_recurse = TRUE;
+ goto again;
+ }
+ else if (errno == EEXIST)
+ {
+ /* Fall through; it may not have been a directory,
+ * but we'll find that out on the next call up.
+ */
+ }
+ else
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+/**
+ * glnx_shutil_mkdir_p_at:
+ * @dfd: Directory fd
+ * @path: Directory path to be created
+ * @mode: Mode for newly created directories
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Similar to g_mkdir_with_parents(), except operates relative to the
+ * directory fd @dfd.
+ */
+gboolean
+glnx_shutil_mkdir_p_at (int dfd,
+ const char *path,
+ int mode,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ struct stat stbuf;
+ char *buf;
+
+ /* Fast path stat to see whether it already exists */
+ if (fstatat (dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == 0)
+ {
+ if (S_ISDIR (stbuf.st_mode))
+ {
+ ret = TRUE;
+ goto out;
+ }
+ }
+
+ buf = strdupa (path);
+
+ if (!mkdir_p_at_internal (dfd, buf, mode, cancellable, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-dirfd.h>
+
+G_BEGIN_DECLS
+
+gboolean
+glnx_shutil_rm_rf_at (int dfd,
+ const char *path,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean
+glnx_shutil_mkdir_p_at (int dfd,
+ const char *path,
+ int mode,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include <glnx-xattrs.h>
+#include <glnx-errors.h>
+#include <glnx-local-alloc.h>
+
+static GVariant *
+variant_new_ay_bytes (GBytes *bytes)
+{
+ gsize size;
+ gconstpointer data;
+ data = g_bytes_get_data (bytes, &size);
+ g_bytes_ref (bytes);
+ return g_variant_new_from_data (G_VARIANT_TYPE ("ay"), data, size,
+ TRUE, (GDestroyNotify)g_bytes_unref, bytes);
+}
+
+static char *
+canonicalize_xattrs (char *xattr_string,
+ size_t len)
+{
+ char *p;
+ GSList *xattrs = NULL;
+ GSList *iter;
+ GString *result;
+
+ result = g_string_new (0);
+
+ p = xattr_string;
+ while (p < xattr_string+len)
+ {
+ xattrs = g_slist_prepend (xattrs, p);
+ p += strlen (p) + 1;
+ }
+
+ xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
+ for (iter = xattrs; iter; iter = iter->next) {
+ g_string_append (result, iter->data);
+ g_string_append_c (result, '\0');
+ }
+
+ g_slist_free (xattrs);
+ return g_string_free (result, FALSE);
+}
+
+static gboolean
+read_xattr_name_array (const char *path,
+ int fd,
+ const char *xattrs,
+ size_t len,
+ GVariantBuilder *builder,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ const char *p;
+ int r;
+ const char *funcstr;
+
+ g_assert (path != NULL || fd != -1);
+
+ funcstr = fd != -1 ? "fgetxattr" : "lgetxattr";
+
+ p = xattrs;
+ while (p < xattrs+len)
+ {
+ ssize_t bytes_read;
+ char *buf;
+ GBytes *bytes = NULL;
+
+ if (fd != -1)
+ bytes_read = fgetxattr (fd, p, NULL, 0);
+ else
+ bytes_read = lgetxattr (path, p, NULL, 0);
+ if (bytes_read < 0)
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", funcstr);
+ goto out;
+ }
+ if (bytes_read == 0)
+ continue;
+
+ buf = g_malloc (bytes_read);
+ bytes = g_bytes_new_take (buf, bytes_read);
+ if (fd != -1)
+ r = fgetxattr (fd, p, buf, bytes_read);
+ else
+ r = lgetxattr (path, p, buf, bytes_read);
+ if (r < 0)
+ {
+ g_bytes_unref (bytes);
+ glnx_set_prefix_error_from_errno (error, "%s", funcstr);
+ goto out;
+ }
+
+ g_variant_builder_add (builder, "(@ay@ay)",
+ g_variant_new_bytestring (p),
+ variant_new_ay_bytes (bytes));
+
+ p = p + strlen (p) + 1;
+ g_bytes_unref (bytes);
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
+get_xattrs_impl (const char *path,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ ssize_t bytes_read;
+ glnx_free char *xattr_names = NULL;
+ glnx_free char *xattr_names_canonical = NULL;
+ GVariantBuilder builder;
+ gboolean builder_initialized = FALSE;
+ g_autoptr(GVariant) ret_xattrs = NULL;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
+ builder_initialized = TRUE;
+
+ bytes_read = llistxattr (path, NULL, 0);
+
+ if (bytes_read < 0)
+ {
+ if (errno != ENOTSUP)
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", "llistxattr");
+ goto out;
+ }
+ }
+ else if (bytes_read > 0)
+ {
+ xattr_names = g_malloc (bytes_read);
+ if (llistxattr (path, xattr_names, bytes_read) < 0)
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", "llistxattr");
+ goto out;
+ }
+ xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
+
+ if (!read_xattr_name_array (path, -1, xattr_names_canonical, bytes_read, &builder, error))
+ goto out;
+ }
+
+ ret_xattrs = g_variant_builder_end (&builder);
+ builder_initialized = FALSE;
+ g_variant_ref_sink (ret_xattrs);
+
+ ret = TRUE;
+ if (out_xattrs)
+ *out_xattrs = g_steal_pointer (&ret_xattrs);
+ out:
+ if (!builder_initialized)
+ g_variant_builder_clear (&builder);
+ return ret;
+}
+
+/**
+ * glnx_fd_get_all_xattrs:
+ * @fd: a file descriptor
+ * @out_xattrs: (out): A new #GVariant containing the extended attributes
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Read all extended attributes from @fd in a canonical sorted order, and
+ * set @out_xattrs with the result.
+ *
+ * If the filesystem does not support extended attributes, @out_xattrs
+ * will have 0 elements, and this function will return successfully.
+ */
+gboolean
+glnx_fd_get_all_xattrs (int fd,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ ssize_t bytes_read;
+ glnx_free char *xattr_names = NULL;
+ glnx_free char *xattr_names_canonical = NULL;
+ GVariantBuilder builder;
+ gboolean builder_initialized = FALSE;
+ g_autoptr(GVariant) ret_xattrs = NULL;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
+ builder_initialized = TRUE;
+
+ bytes_read = flistxattr (fd, NULL, 0);
+
+ if (bytes_read < 0)
+ {
+ if (errno != ENOTSUP)
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", "flistxattr");
+ goto out;
+ }
+ }
+ else if (bytes_read > 0)
+ {
+ xattr_names = g_malloc (bytes_read);
+ if (flistxattr (fd, xattr_names, bytes_read) < 0)
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", "flistxattr");
+ goto out;
+ }
+ xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
+
+ if (!read_xattr_name_array (NULL, fd, xattr_names_canonical, bytes_read, &builder, error))
+ goto out;
+ }
+
+ ret_xattrs = g_variant_builder_end (&builder);
+ builder_initialized = FALSE;
+ g_variant_ref_sink (ret_xattrs);
+
+ ret = TRUE;
+ if (out_xattrs)
+ *out_xattrs = g_steal_pointer (&ret_xattrs);
+ out:
+ if (!builder_initialized)
+ g_variant_builder_clear (&builder);
+ return ret;
+}
+
+/**
+ * glnx_dfd_name_get_all_xattrs:
+ * @dfd: Parent directory file descriptor
+ * @name: File name
+ * @out_xattrs: (out): Extended attribute set
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Load all extended attributes for the file named @name residing in
+ * directory @dfd.
+ */
+gboolean
+glnx_dfd_name_get_all_xattrs (int dfd,
+ const char *name,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (dfd == AT_FDCWD || dfd == -1)
+ {
+ return get_xattrs_impl (name, out_xattrs, cancellable, error);
+ }
+ else
+ {
+ char buf[PATH_MAX];
+ /* A workaround for the lack of lgetxattrat(), thanks to Florian Weimer:
+ * https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html
+ */
+ snprintf (buf, sizeof (buf), "/proc/self/fd/%d/%s", dfd, name);
+ return get_xattrs_impl (buf, out_xattrs, cancellable, error);
+ }
+}
+
+static gboolean
+set_all_xattrs_for_path (const char *path,
+ GVariant *xattrs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ int i, n;
+
+ n = g_variant_n_children (xattrs);
+ for (i = 0; i < n; i++)
+ {
+ const guint8* name;
+ g_autoptr(GVariant) value = NULL;
+ const guint8* value_data;
+ gsize value_len;
+
+ g_variant_get_child (xattrs, i, "(^&ay@ay)",
+ &name, &value);
+ value_data = g_variant_get_fixed_array (value, &value_len, 1);
+
+ if (lsetxattr (path, (char*)name, (char*)value_data, value_len, 0) < 0)
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", "lsetxattr");
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+/**
+ * glnx_dfd_name_set_all_xattrs:
+ * @dfd: Parent directory file descriptor
+ * @name: File name
+ * @xattrs: Extended attribute set
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Set all extended attributes for the file named @name residing in
+ * directory @dfd.
+ */
+gboolean
+glnx_dfd_name_set_all_xattrs (int dfd,
+ const char *name,
+ GVariant *xattrs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (dfd == AT_FDCWD || dfd == -1)
+ {
+ return set_all_xattrs_for_path (name, xattrs, cancellable, error);
+ }
+ else
+ {
+ char buf[PATH_MAX];
+ /* A workaround for the lack of lsetxattrat(), thanks to Florian Weimer:
+ * https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html
+ */
+ snprintf (buf, sizeof (buf), "/proc/self/fd/%d/%s", dfd, name);
+ return set_all_xattrs_for_path (buf, xattrs, cancellable, error);
+ }
+}
+
+/**
+ * glnx_fd_set_all_xattrs:
+ * @fd: File descriptor
+ * @xattrs: Extended attributes
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * For each attribute in @xattrs, set its value on the file or
+ * directory referred to by @fd. This function does not remove any
+ * attributes not in @xattrs.
+ */
+gboolean
+glnx_fd_set_all_xattrs (int fd,
+ GVariant *xattrs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ int i, n;
+
+ n = g_variant_n_children (xattrs);
+ for (i = 0; i < n; i++)
+ {
+ const guint8* name;
+ const guint8* value_data;
+ g_autoptr(GVariant) value = NULL;
+ gsize value_len;
+ int res;
+
+ g_variant_get_child (xattrs, i, "(^&ay@ay)",
+ &name, &value);
+ value_data = g_variant_get_fixed_array (value, &value_len, 1);
+
+ do
+ res = fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0);
+ while (G_UNLIKELY (res == -1 && errno == EINTR));
+ if (G_UNLIKELY (res == -1))
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", "fsetxattr");
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+/**
+ * glnx_lgetxattrat:
+ * @dfd: Directory file descriptor
+ * @subpath: Subpath
+ * @attribute: Extended attribute to retrieve
+ * @error: Error
+ *
+ * Retrieve an extended attribute value, relative to a directory file
+ * descriptor.
+ */
+GBytes *
+glnx_lgetxattrat (int dfd,
+ const char *subpath,
+ const char *attribute,
+ GError **error)
+{
+ char pathbuf[PATH_MAX];
+ GBytes *bytes = NULL;
+ ssize_t bytes_read, real_size;
+ guint8 *buf;
+
+ snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath);
+
+ do
+ bytes_read = lgetxattr (pathbuf, attribute, NULL, 0);
+ while (G_UNLIKELY (bytes_read < 0 && errno == EINTR));
+ if (G_UNLIKELY (bytes_read < 0))
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ buf = g_malloc (bytes_read);
+ do
+ real_size = lgetxattr (pathbuf, attribute, buf, bytes_read);
+ while (G_UNLIKELY (real_size < 0 && errno == EINTR));
+ if (G_UNLIKELY (real_size < 0))
+ {
+ glnx_set_error_from_errno (error);
+ g_free (buf);
+ goto out;
+ }
+
+ bytes = g_bytes_new_take (buf, real_size);
+ out:
+ return bytes;
+}
+
+/**
+ * glnx_fgetxattr_bytes:
+ * @fd: Directory file descriptor
+ * @attribute: Extended attribute to retrieve
+ * @error: Error
+ *
+ * Returns: (transfer full): An extended attribute value, or %NULL on error
+ */
+GBytes *
+glnx_fgetxattr_bytes (int fd,
+ const char *attribute,
+ GError **error)
+{
+ GBytes *bytes = NULL;
+ ssize_t bytes_read, real_size;
+ guint8 *buf;
+
+ do
+ bytes_read = fgetxattr (fd, attribute, NULL, 0);
+ while (G_UNLIKELY (bytes_read < 0 && errno == EINTR));
+ if (G_UNLIKELY (bytes_read < 0))
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ buf = g_malloc (bytes_read);
+ do
+ real_size = fgetxattr (fd, attribute, buf, bytes_read);
+ while (G_UNLIKELY (real_size < 0 && errno == EINTR));
+ if (G_UNLIKELY (real_size < 0))
+ {
+ glnx_set_error_from_errno (error);
+ g_free (buf);
+ goto out;
+ }
+
+ bytes = g_bytes_new_take (buf, real_size);
+ out:
+ return bytes;
+}
+
+/**
+ * glnx_lsetxattrat:
+ * @dfd: Directory file descriptor
+ * @subpath: Path
+ * @attribute: An attribute name
+ * @value: (array length=len) (element-type guint8): Attribute value
+ * @len: Length of @value
+ * @flags: Flags, containing either XATTR_CREATE or XATTR_REPLACE
+ * @error: Error
+ *
+ * Set an extended attribute, relative to a directory file descriptor.
+ */
+gboolean
+glnx_lsetxattrat (int dfd,
+ const char *subpath,
+ const char *attribute,
+ const guint8 *value,
+ gsize len,
+ int flags,
+ GError **error)
+{
+ char pathbuf[PATH_MAX];
+ int res;
+
+ snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath);
+
+ do
+ res = lsetxattr (subpath, attribute, value, len, flags);
+ while (G_UNLIKELY (res == -1 && errno == EINTR));
+ if (G_UNLIKELY (res == -1))
+ {
+ glnx_set_error_from_errno (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-backport-autocleanups.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/xattr.h>
+
+G_BEGIN_DECLS
+
+gboolean
+glnx_dfd_name_get_all_xattrs (int dfd,
+ const char *name,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean
+glnx_fd_get_all_xattrs (int fd,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean
+glnx_dfd_name_set_all_xattrs (int dfd,
+ const char *name,
+ GVariant *xattrs,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean
+glnx_fd_set_all_xattrs (int fd,
+ GVariant *xattrs,
+ GCancellable *cancellable,
+ GError **error);
+
+GBytes *
+glnx_lgetxattrat (int dfd,
+ const char *subpath,
+ const char *attribute,
+ GError **error);
+
+GBytes *
+glnx_fgetxattr_bytes (int fd,
+ const char *attribute,
+ GError **error);
+
+gboolean
+glnx_lsetxattrat (int dfd,
+ const char *subpath,
+ const char *attribute,
+ const guint8 *value,
+ gsize len,
+ int flags,
+ GError **error);
+
+G_END_DECLS
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
+ xmlns:gnome="http://api.gnome.org/doap-extensions#"
+ xmlns="http://usefulinc.com/ns/doap#">
+
+ <name>libglnx</name>
+ <shortname>libglnx</shortname>
+
+ <shortdesc xml:lang="en">"Copylib" for system service modules using GLib with Linux</shortdesc>
+
+ <description xml:lang="en">This module is intended for use by
+ infrastructure code using GLib that is also Linux specific, such as
+ ostree, NetworkManager, and others.
+ </description>
+
+ <license rdf:resource="http://usefulinc.com/doap/licenses/lgpl" />
+ <mailing-list rdf:resource="mailto:desktop-devel-list@gnome.org" />
+
+ <programming-language>C</programming-language>
+
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Colin Walters</foaf:name>
+ <foaf:mbox rdf:resource="mailto:walters@verbum.org"/>
+ <gnome:userid>walters</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+
+</Project>
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012,2013,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#include <glnx-local-alloc.h>
+#include <glnx-backport-autocleanups.h>
+#include <glnx-backports.h>
+#include <glnx-lockfile.h>
+#include <glnx-errors.h>
+#include <glnx-dirfd.h>
+#include <glnx-shutil.h>
+#include <glnx-xattrs.h>
+#include <glnx-libcontainer.h>
+#include <glnx-console.h>
+#include <glnx-fdio.h>
+
+G_END_DECLS